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.
Each request is billed at 10 units.
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/normalized matches; standard adds close fuzzy and strong phonetic matches; broad adds weaker fuzzy matches. |
limit | number | Page size from 1–50. Default: 20. |
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. 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 the analysis horizon was capped. It is separate fromhas_more, which describes response pagination.- 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);
}