Screen a Name
curl --request GET \
--url https://api.signa.so/v1/screening \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.signa.so/v1/screening"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.signa.so/v1/screening', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.signa.so/v1/screening",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.signa.so/v1/screening"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.signa.so/v1/screening")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.signa.so/v1/screening")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_bodyScreening
Screen a Name
Run a fast, deterministic knockout screen for one candidate name (alpha, internal testing)
GET
/
v1
/
screening
Screen a Name
curl --request GET \
--url https://api.signa.so/v1/screening \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.signa.so/v1/screening"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.signa.so/v1/screening', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.signa.so/v1/screening",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.signa.so/v1/screening"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.signa.so/v1/screening")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.signa.so/v1/screening")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_bodyAlpha, internal testing. Screening is not open to customers yet. These endpoints run on internal and design-partner keys during internal testing and their
request and response shapes can still change. Email
support@signa.so to join the early access list.
Screening returns a risk signal, not legal advice.
Overview
GET /v1/screening answers “could this candidate name conflict with an
existing mark?” Send a name and, optionally, its intended Nice classes or
goods/services. Screening searches live register coverage, assigns each
surfaced conflict a coarse band, and derives one request-level verdict.
The endpoint is synchronous, deterministic within its rules and corpus
versions, and text-only. It never returns a numeric similarity score. A
clear verdict means that no high- or medium-band conflict was found; it does
not mean that the mark is registrable.
Screening is in beta: each request is metered once per screen, whatever the
office scope, but not charged today.
Request
curl --get https://api.signa.so/v1/screening \
--header "Authorization: Bearer $SIGNA_API_KEY" \
--data-urlencode "q=KOFEE" \
--data-urlencode "nice_classes=30,43" \
--data-urlencode "jurisdictions=US,EU" \
--data-urlencode "sensitivity=standard"
| Parameter | Type | Notes |
|---|---|---|
q | string | Candidate mark text; 2–200 non-whitespace characters. Exactly one of q or trademark_id is required. |
trademark_id | string | Existing register record (tm_...) to use as the candidate. Exactly one of q or trademark_id is required. |
nice_classes | number[] | Intended Nice classes, 1–45. Sent as a comma-separated list. Acts as a hard class filter. |
goods_services | string | Free-text intended goods/services, up to 2,000 characters. Used for class inference when classes are omitted and for goods/services matching when supported by the active resolver. |
jurisdictions | string[] | Territory codes such as US and EU. Default: all live-coverage jurisdictions. Screening always applies protection-scope expansion, and unlike Search Trademarks there is no territory_match opt-out: a request for a country pulls in EU-wide rights that protect there (a EUTM can block a French launch), and a request for a region (EU) pulls in both the regional register and every member state’s national register (a French national mark can block an EU launch). Screening always errs toward inclusion. |
offices | string[] | Registering office codes, uppercase ST.3, such as US and EM. Default: all live offices. |
include | live | all | live by default; all also includes dead marks. |
sensitivity | strict | standard | broad | Match-tier admission threshold. strict admits exact and normalized matches only. standard adds close fuzzy, phonetic and other looser match levels, and broad adds weaker ones still. The exact set depends on the screening version: the response lists it in admitted_match_levels. |
limit | number | Results returned, from 1 to 50. Default: 20. Screening does not page: raise limit to see more. |
Screen an existing mark
Usetrademark_id instead of q for register-to-register discovery. Screening
uses the record’s mark text and, when you omit both nice_classes and
goods_services, its own Nice classes as the intended-use profile. The
candidate’s collapsed family is removed before conflicts are banded, so it
cannot appear as its own result.
curl --get https://api.signa.so/v1/screening \
--header "Authorization: Bearer $SIGNA_API_KEY" \
--data-urlencode "trademark_id=tm_0190f2d5-8e4b-7c1a-a8d3-123456789abc"
screening.candidate.trademark_id. Unknown
records return 404 not_found; records without textual mark content, such as
design-only marks, return 422 not_screenable.
Bands and verdicts
Every item indata[] is a screening_hit with one closed risk band:
| Band | Meaning |
|---|---|
high | Strongest combination of name match, goods/services overlap, and live status. |
medium | A meaningful conflict signal that needs review. |
low | A surfaced but lower-priority signal, including admitted dead-mark results. |
screening.verdict summarizes the full banded result set, not only
the returned page:
| Verdict | Rule |
|---|---|
high_risk | At least one high-band conflict exists. |
caution | No high-band conflict exists, but at least one medium-band conflict exists. |
needs_review | No high- or medium-band conflict was found, but the screen could not be completed to the standard required to say clear. The usual cause is incomplete register coverage for a requested territory; a stale office corpus, a capped analysis horizon, a failed analysis lane, an unsupported language, low-confidence class inference, or a coverage assessment that could not be read at all produce it too. When a coverage assessment is present, the coverage block carries the details; it can be absent when the assessment itself failed. |
clear | No high- or medium-band conflict exists and every requested territory’s register is covered live in Signa. Clear does not mean registrable. |
clear,
it never upgrades a genuine conflict. Treat needs_review as “we could not rule
this out”, not as a weaker clear.
Hits also explain the coarse classification through reason_codes,
mark_match, goods_services_match, jurisdiction_match, and a separate
litigation_risk block. Reason codes and warnings are extensible; clients
should tolerate values they do not yet recognize.
Coverage
A clearance verdict for a territory is only as trustworthy as the registers Signa can actually see there. If the national register that protects a requested territory is not connected live (for example a request scoped toDE when the
German national register is dark), national-only marks in that territory are
invisible, so a zero-conflict screen must not read as clear. The
screening.coverage block reports exactly which registers gated the verdict.
"coverage": {
"offices": [
{ "office": "US", "status": "complete", "as_of": "2026-07-20T00:00:00Z" },
{ "office": "EM", "status": "complete", "as_of": "2026-07-20T00:00:00Z" }
],
"territories": [
{
"territory": "DE",
"national_register": { "office": "DE", "covered": false },
"regional_registers": [{ "code": "EU", "covered": true }],
"complete": false
}
],
"complete_for_clear": false
}
coverage object
also always includes classes, lanes, language, and horizon, the other
completeness inputs that can hold a verdict at needs_review.
officesis the register-freshness axis. Each entry is a public office code in WIPO ST.3 form (US,EMfor the EUIPO), with astatus(complete/partial/stale/unavailable) and anas_ofcorpus watermark. It lists the offices that gated this verdict: the requested offices whenofficeswas supplied, the national plus regional and member registers whenjurisdictionswas supplied, or every live office when the request is unscoped.territoriesis the protection-scope axis, present whenjurisdictionswas supplied. Codes here are jurisdiction codes (DE,EU), not ST.3 office codes. For each requested territory it reports its ownnational_register, theregional_registersthat also protect there (a country belonging toEUshows anEUentry), and whether the whole set iscomplete.- For a regional request (
jurisdictions=EU) a territory entry addsmember_registers: { total, uncovered[] }, summarizing how many member-state national registers are dark;regional_registersis still present but empty (the region is its own register). An EU screen staysneeds_reviewwhile member registers are uncovered, and flips toclearonly once they are live and fresh, the same principle as theDEexample. classesechoes the classes.requestedis what you sent innice_classes.resolvedlists the classes matched from yournice_classesplus yourgoods_servicestext.searchedis the class filter applied by the class-filtered retrieval lanes, which can include classes matched from yourgoods_servicestext; the containment lane, which finds marks that contain the name, is not class-filtered and searches every class.expandedlists the classessearchedadds beyondrequested(or, withoutnice_classes, beyond the record’s or the inferred classes). Atrademark_idscreen withoutnice_classesorgoods_servicessearches the record’s own classes and lists them inresolvedandsearched;requestedstays empty, andsourcestill readsrequestedbecause there is no separate value for record classes.languagereports the main script of the mark (latin,cyrillic,cjk,thai,arabicorother). Screening matches Latin text, so when the non-Latin letters, all scripts together, outnumber the Latin letters,supportedisfalseand a would-beclearbecomesneeds_reviewwith theunsupported_languagewarning. Accented and fullwidth Latin letters count as Latin.horizongives the conflicts analysed (analyzed) and the most one screen analyses (cap).cappedistruewhen the overall horizon or a retrieval lane’s quota had more matches than it could analyse, so it can betruewithanalyzedbelowcap.lanesis reserved; its lists are empty until per-lane status ships.complete_for_clearis the single boolean the verdict gate reads. When it isfalse, a would-beclearis downgraded toneeds_reviewandscreening.notenames the dark register.coverageis present on every screened response. When the coverage assessment itself cannot be completed, the block fails closed withcomplete_for_clear: falseand an emptyoffices, which signals that coverage could not be verified and forces the verdict toneeds_review.
Response
{
"object": "list",
"screening": {
"verdict": "high_risk",
"summary": { "high": 1, "medium": 0, "low": 0, "truncated": false },
"litigation_risk": { "high": 0, "medium": 0 },
"candidate": { "mark": "KOFEE", "normalized": "kofee" },
"rules_version": "screening-v1",
"as_of": "2026-07-13T00:00:00.000Z",
"admitted_match_levels": ["exact", "normalized", "fuzzy_1", "phonetic_strong"],
"coverage": {
"offices": [{ "office": "US", "status": "complete", "as_of": "2026-07-20T00:00:00Z" }],
"territories": [
{ "territory": "US", "national_register": { "office": "US", "covered": true }, "regional_registers": [], "complete": true }
],
"complete_for_clear": true
},
"warnings": []
},
"data": [
{
"object": "screening_hit",
"risk_level": "high",
"reason_codes": ["exact_mark", "same_nice_class", "live_status"],
"mark_match": { "level": "identical", "admitted_match_levels": ["exact"] },
"goods_services_match": { "level": "same_class", "matched_nice_classes": [30] },
"jurisdiction_match": { "requested": ["US", "EU"], "matched": ["US"] },
"litigation_risk": {
"level": "unknown",
"owner_publicly_traded": null,
"owner_ticker": null,
"prior_proceedings": null,
"owner_portfolio_size": null
},
"trademark": { "id": "tm_…", "object": "trademark", "mark_text": "KOFEE" }
}
],
"has_more": false,
"pagination": { "cursor": null },
"request_id": "req_…"
}
screening.rules_version and screening.as_of identify the rules and corpus
freshness behind the result. screening.inferred_nice_classes appears only
when class inference ran (i.e. nice_classes was omitted from the request).
Warnings and limitations
warningsis an open set. Handle known values and safely display or log new values rather than rejecting the response.- A missing goods/services description may produce
no_goods_services; the result then has less context for class and goods/services matching. summary.truncated: truemeans more conflicts were found thanlimitreturned. Screening does not page (has_moreis alwaysfalse), so raiselimit, up to 50, to see more. The verdict and thesummarycounts already cover every conflict found. Whether the analysis itself was capped isscreening.coverage.horizon.capped.- A
crowded_fielddemotion counts only the owners of marks that match your name exactly or after normalization. Similar but different marks are not counted. - Screening v1 is text-only. It does not compare logos, designs, Vienna codes, or images.
- Backend failures return an error rather than a partial or false
clear. - Screening responses are served
Cache-Control: private, no-store. A screening result reflects live register coverage at request time, so it is not safe to cache client-side, re-run the screen rather than replaying a stored response.
SDK
const result = await signa.screening.screen({
q: "KOFEE",
nice_classes: [30, 43],
jurisdictions: ["US", "EU"],
sensitivity: "standard",
});
console.log(result.screening.verdict);
for (const hit of result.data) {
console.log(hit.trademark.mark_text, hit.risk_level);
}