Search Trademarks
curl --request GET \
--url https://api.signa.so/v1/trademarks \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.signa.so/v1/trademarks"
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/trademarks', 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/trademarks",
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/trademarks"
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/trademarks")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.signa.so/v1/trademarks")
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_body{
"object": "list",
"data": [
{
"id": "tm_8kLm2nPq",
"object": "trademark",
"mark_text": "TIMBER",
"status": { "primary": "active", "stage": "registered" },
"office_code": "US",
"application_number": "97134218",
"filing_date": "2021-11-30",
"nice_classes": [9],
"owners": [{ "id": "own_R3jK9mN2", "name": "Timber Labs, Inc." }],
"relevance_score": 96,
"match": { "tier": "identical", "via": ["identical"], "terms": [] }
},
{
"id": "tm_4hQx7wRt",
"object": "trademark",
"mark_text": "TIMBRE",
"status": { "primary": "pending", "stage": "examining" },
"office_code": "EM",
"application_number": "019295452",
"filing_date": "2026-02-03",
"nice_classes": [9, 42],
"owners": [{ "id": "own_P2mX8vQ1", "name": "Timbre Audio GmbH" }],
"relevance_score": 71,
"match": { "tier": "near_identical", "via": ["fuzzy"], "terms": [] }
}
],
"has_more": true,
"pagination": { "cursor": "eyJpZCI6ImFiYyJ9" },
"search_meta": {
"similarity_applied": ["identical", "fuzzy", "embedded", "lookalike"],
"order": "relevance",
"complete": true,
"international_registrations": "grouped"
},
"request_id": "req_xyz789"
}
Search
Search Trademarks
Search and filter trademarks across every office
GET
/
v1
/
trademarks
Search Trademarks
curl --request GET \
--url https://api.signa.so/v1/trademarks \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.signa.so/v1/trademarks"
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/trademarks', 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/trademarks",
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/trademarks"
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/trademarks")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.signa.so/v1/trademarks")
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_body{
"object": "list",
"data": [
{
"id": "tm_8kLm2nPq",
"object": "trademark",
"mark_text": "TIMBER",
"status": { "primary": "active", "stage": "registered" },
"office_code": "US",
"application_number": "97134218",
"filing_date": "2021-11-30",
"nice_classes": [9],
"owners": [{ "id": "own_R3jK9mN2", "name": "Timber Labs, Inc." }],
"relevance_score": 96,
"match": { "tier": "identical", "via": ["identical"], "terms": [] }
},
{
"id": "tm_4hQx7wRt",
"object": "trademark",
"mark_text": "TIMBRE",
"status": { "primary": "pending", "stage": "examining" },
"office_code": "EM",
"application_number": "019295452",
"filing_date": "2026-02-03",
"nice_classes": [9, 42],
"owners": [{ "id": "own_P2mX8vQ1", "name": "Timbre Audio GmbH" }],
"relevance_score": 71,
"match": { "tier": "near_identical", "via": ["fuzzy"], "terms": [] }
}
],
"has_more": true,
"pagination": { "cursor": "eyJpZCI6ImFiYyJ9" },
"search_meta": {
"similarity_applied": ["identical", "fuzzy", "embedded", "lookalike"],
"order": "relevance",
"complete": true,
"international_registrations": "grouped"
},
"request_id": "req_xyz789"
}
Credits: 10 per page.
Find marks that resemble a name with
A
With
Every mark that equals one of three spellings:
Some requests can only be answered per country: aggregations, most sorts, and some filter combinations such as
q, keep or drop marks by their exact text with text filters and exclusions, and narrow by office, class, status, owner or date. Every request needs q, a text filter, or at least one other filter.
curl -G "https://api.signa.so/v1/trademarks" \
-H "Authorization: Bearer $SIGNA_API_KEY" \
--data-urlencode "q=timber" \
--data-urlencode "nice_classes=9,42" \
--data-urlencode "status_primary=active,pending"
Search changed on September 29, 2026. The old parameters (
strategies, match) keep working through December 27, 2026. See the migration guide.Search by name
q finds marks that look or sound like a name, best match first. To keep only marks whose text matches exactly, such as starts with or contains, use text filters. You can use one or both.
Similarity
q returns marks that resemble it, closest first. similarity chooses how they may resemble it:
| Channel | Finds | Example for timber |
|---|---|---|
identical | The same text, ignoring case, accents, punctuation, spacing and ® | TIMBER, Timber®, TIM-BER |
fuzzy | One or two changed letters | TIMBRE, TYMBER, LIMBER |
embedded | The name as a word, phrase or start of a word in a longer mark | TIMBERLAND, OLD TIMBER CO |
phonetic | Marks that sound the same | TYMBUR |
lookalike | Look-alike characters, such as 3 for E, or Cyrillic А in a Latin name | TIMB3R |
embedded matches the name as you spell it. A name written as one word does not find the same name split by a hyphen or space inside a longer mark: q=cocacola misses COCA-COLA ZERO, which q=coca-cola finds. Search each spelling (send q as a list), or send mark_text_contains=cocacola without q: an unranked list of marks containing the name, ignoring punctuation and spacing. Adding mark_text_contains to q only narrows the results q found; it does not add the missed marks.
A q written only in Cyrillic or Greek letters, such as Кока-Кола, gets look-alike matches of the whole mark only, not look-alike matches with a changed letter.
The default is every channel except phonetic, which adds many distant matches. identical always runs. For a more thorough search, turn on all five:
curl -G "https://api.signa.so/v1/trademarks" \
-H "Authorization: Bearer $SIGNA_API_KEY" \
--data-urlencode "q=timber" \
--data-urlencode "similarity=identical,fuzzy,embedded,phonetic,lookalike" \
--data-urlencode "jurisdictions=US,EU" \
--data-urlencode "nice_classes=9"
q of several words generally requires its words to appear in the mark, each as its similarity channels allow, so a mark that shares only some of them is usually missed: q=stone lion capital does not find LION CAPITAL. To widen the search, send q as a list of shorter phrases, such as q=["stone lion","lion capital"], which ranks the marks of each phrase together. A common single word in the list returns many marks that share only that word. When a q of several words returns fewer than 5 rows in all, search_meta.warnings on page 1 carries all_words_required (not when phonetic is among the channels, which also finds marks sharing one sound-alike word), and multi_word_retry_skipped when Signa skipped its second search (one that allows a one-letter difference in each word) because the first took too long.
q is plain text, with no operators or wildcards. It takes 2 to 500 characters and up to 22 words. You can also paste an application, registration or international registration number into q, such as 97134218, 79412883 or IR 1939139: the matching records come first, with identifier in match.via.
A number in q is a lookup. Unless you set international_registrations, it works like this:
- A US serial or registration number returns that US record, with its own US status. This includes a US designation of an international registration (a 79-series serial).
- A labelled international registration number, such as
IR 1939139, returns one row for the international registration and its designations (the grouped view). - A bare number returns the records it names, one row per record, each shown the way its detail is. Records whose own number is the one you searched come first. For an international registration number, these are the international registration (
grain: "mark") and any national record with the same number. Designations that only carry the international registration number follow. - A
tm_ID returns that record.
identifier_lookup warning.
Several names at once
Sendq as a list to rank up to 100 names in one search, such as every spelling of a mark you are clearing. Each name runs through the same similarity channels. A mark that resembles several names appears once, ranked by its closest name, and match.terms lists the names that found it.
curl -G "https://api.signa.so/v1/trademarks" \
-H "Authorization: Bearer $SIGNA_API_KEY" \
--data-urlencode 'q=["onyx","0nyx","onix"]' \
--data-urlencode "similarity=identical,lookalike"
const variants = await signa.trademarks.list({
q: ["onyx", "0nyx", "onix"],
similarity: ["identical", "lookalike"],
});
for (const mark of variants.data) {
console.log(mark.mark_text, mark.match?.terms, mark.match?.via);
}
lookalike, onyx also finds 0NYX (a zero for the O).
- Up to 100 names when
similarityis onlyidenticalandlookalike; withfuzzy,embeddedorphonetic(including the default), up to 10 names and 30 words. Over the limit returns400(too_bigortoo_complex). - Send long lists with
POST:GETURLs are limited to 2 KB. A list is never read as a trademark number. - Results are ranked across all names, so a page is not complete per name. A name with no hits on the pages you read is not proof it is clear: page to the end, or search it on its own.
- A list skips two single-
qboosts (similar length, word starts with the name) and the word-by-wordfuzzyretry.include=match_detailsadds each name’s own tier and channels inmatch.details.terms.
string | string[]
The name to search for, or a list of up to 100 names.
string
default:"identical,fuzzy,embedded,lookalike"
Channels, comma-separated. Requires
q.Filters
Filters narrow a search, with or withoutq. Every filter must match. Most take comma-separated lists, such as nice_classes=9,42. Not every office publishes every field; List Offices shows coverage per office.
Offices and jurisdictions
There are two ways to filter by place:jurisdictionsfinds marks that protect a territory, however they were filed.jurisdictions=FRreturns French national marks, EU trade marks, and Madrid registrations designating France.officesfinds records filed at a specific office.offices=FRreturns only what the French office publishes.
jurisdictions to see everything that covers a market. Use offices when you need one office’s own register.
A country code also matches the regional rights that cover it, but a regional code is matched strictly. jurisdictions=NL returns rights protecting the Netherlands: marks whose territories include NL, EU trade marks and Benelux (BX) rights. jurisdictions=BX returns Benelux rights only and leaves out EU trade marks, even though a EUTM also protects the Benelux. For a Benelux clearance, request jurisdictions=BX,EU, or use Screening, which adds the overlapping regions for you.
curl -G "https://api.signa.so/v1/trademarks" \
-H "Authorization: Bearer $SIGNA_API_KEY" \
--data-urlencode "q=nova" \
--data-urlencode "jurisdictions=FR,DE"
string
Territories the mark protects, such as
US, EU or FR. List Jurisdictions has every code.string
Offices the record was filed at, as ST.3 codes such as
US, EM or WO. List Offices has every office we cover.string
default:"protection"
Set to
direct to match only rights filed for the exact territory, leaving out regional rights such as EU trade marks.Classes, goods and services
Filter by Nice class to find marks covering the same kind of goods or services. If you only have a description, Suggest Classifications turns it into classes, for example “running shoes” into class 25.string
Nice classes, 1 to 45.
string
Words in the goods and services description, such as
footwear.Status
Every office uses its own status codes. Signa maps them all onto one status model, so the same filter works across offices. The office’s own code stays instatus.raw.
status_primaryis the broad status. For marks that are still alive, useactive,pending: registered marks and pending applications.status_stageis where the mark is in its lifecycle, such aspublishedorregistered.status_reasonsays why a mark ended, such asrefusedorexpired, andchallenge_stateslists open challenges, such asopposition_pending.opposition_status=openfinds marks that can be opposed right now.
string
active, pending, inactive or unknown.string
Such as
examining, published, registered, abandoned or expired.string
Such as
refused, withdrawn, abandoned, cancelled or expired.string
Such as
opposition_pending, cancellation_pending or appeal_pending.string
open, not_started, closed or unknown.Text filters
Text filters keep only marks whose text passes a test. They don’t rank or match similar spellings. Use them alone for a plain list, or withq to narrow a ranked search. Repeat a key to check several values at once, such as a list of spellings: mark_text_is=nike&mark_text_is=mike. Each result lists the values it matched in match.terms. To rank a list of spellings, with look-alike characters and close spellings, send them as a list in q.
All text filters
All text filters
| Parameter | Keeps marks where | nova matches |
|---|---|---|
mark_text_is | The whole mark equals the value | NOVA, Nova®, NO-VA |
mark_text_starts_with | The mark begins with the value | NOVARTIS, NOVA SCOTIA |
mark_text_contains | The value appears anywhere | CASANOVA, SUPERNOVAS |
mark_text_word | The value is a whole word of the mark | NOVA SCOTIA, BOSSA NOVA, not NOVARTIS |
mark_text_ends_with | The mark ends with the value | BOSSA NOVA, CASANOVA |
mark_text_pattern | The mark fits a pattern, with * for any characters and ? for one | NOV? finds NOVA and NOVO |
curl -G "https://api.signa.so/v1/trademarks" \
-H "Authorization: Bearer $SIGNA_API_KEY" \
--data-urlencode "mark_text_is=nike" \
--data-urlencode "mark_text_is=mike" \
--data-urlencode "mark_text_is=bike"
const variants = await signa.trademarks.list({
mark_text_is: ["nike", "mike", "bike"],
});
for (const mark of variants.data) {
console.log(mark.mark_text, mark.match?.terms);
}
Exclusions
Exclusions
Exclusions leave marks out of the results, for example your client’s own marks. A request needs something besides exclusions.
curl -G "https://api.signa.so/v1/trademarks" \
-H "Authorization: Bearer $SIGNA_API_KEY" \
--data-urlencode "q=nova" \
--data-urlencode "mark_text_not_contains=novartis" \
--data-urlencode "entity_id_not=ent_Wp8qLd4Z"
| Parameter | Leaves out |
|---|---|
mark_text_not_is, _not_word, _not_starts_with, _not_ends_with, _not_contains, _not_pattern | Marks matching the value, like the text filters |
owner_id_not | Marks held by these owner records (own_...) |
entity_id_not | Marks held by these companies (ent_...) |
entity_group_not | Marks held by these companies’ corporate families (ent_...) |
trademark_ids_not | These trademarks (tm_...), up to 1,000. Send more than about 30 with POST |
Matching rules and limits
Matching rules and limits
- Every filter ignores case and accents. All except
mark_text_wordalso ignore punctuation and spacing, somark_text_contains=cacolamatches COCA-COLA. - A mark passes when it matches any value of a filter. Commas are part of the value.
- A filter takes up to 100 values of up to 200 characters.
GETURLs are limited to 2 KB, which holds roughly 30 IDs (eachtrademark_ids_not=tm_...takes 58 characters), so send longer lists withPOST, which accepts bodies up to 64 KB: enough for 1,000trademark_ids. mark_text_contains,mark_text_ends_withand patterns that start with a wildcard need at least 3 characters, and a request can use at most 4 of these values, exclusions included.
Dates
Date filters take a_gte (on or after) or _lt (before) suffix, such as filing_date_gte, with dates as YYYY-MM-DD. Every live US mark in class 9 filed this year:
curl -G "https://api.signa.so/v1/trademarks" \
-H "Authorization: Bearer $SIGNA_API_KEY" \
--data-urlencode "jurisdictions=US" \
--data-urlencode "nice_classes=9" \
--data-urlencode "status_primary=active,pending" \
--data-urlencode "filing_date_gte=2026-01-01"
All date filters
All date filters
Each takes
_gte and _lt, and also _gt and _lte.| Filter | Date |
|---|---|
filing_date | Filed |
registration_date | Registered |
publication_date | Published for opposition |
designation_date | Designated under the Madrid Protocol (designation records only; results are one row per record) |
priority_date | Priority claimed |
expiry_date | Expires, as published by the office. The USPTO publishes none, so USPTO records never match; read derived.expiry_date on each record, or list a portfolio’s computed deadlines with List Deadlines |
renewal_due_date | Renewal due, as published by the office. For USPTO records it matches only Madrid extensions (79-series) that carry the international renewal date the USPTO reports, never a domestic Section 8 or 9 date; read derived.deadlines.items on each record, or list a portfolio’s computed deadlines with List Deadlines |
status_effective_date | Current status took effect |
termination_date | Ended |
first_use_anywhere_date, first_use_in_commerce_date | First used (US) |
updated_at | Record last updated |
opposition_closes | Opposition window closes (_gte and _lte) |
Advanced filters
Owners and representatives
Owners and representatives
To find an owner’s or company’s ID, use Search Owners or Search Entities.
| Filter | Keeps marks |
|---|---|
owner_id | Held by this owner record (own_...) |
entity_id | Held by this company at any office (ent_...) |
entity_group | Held by any company in this corporate family (ent_...) |
owner_name | Whose owner name contains these words |
owner_country | Whose owner is in this country. EUIPO publishes no owner country, so EUIPO records never match; the response then carries a partial_owner_country_coverage warning |
owner_ticker | Held by this listed company or its subsidiaries, e.g. NKE |
owner_lei | Held by the owner with this LEI |
owner_has_lei | true for owners with a GLEIF LEI |
owner_publicly_traded | true for owners linked to a listed company |
attorney_id | Handled by this attorney (att_...) |
firm_id | Handled by this law firm (firm_...): the filing names the firm. A filing that names no firm matches no firm_id |
attorney_firm_name | The firm name the filing states for its first attorney (fuzzy) |
Mark type and design
Mark type and design
| Filter | Keeps marks |
|---|---|
mark_feature_type | Of this type, such as word, figurative, combined, three_d, color or sound |
mark_legal_category | In this category, such as standard, collective or certification |
vienna_codes | With these Vienna figurative codes |
us_design_codes | With these USPTO design search codes |
has_media | true for marks with an image |
Numbers and filing details
Numbers and filing details
| Filter | Keeps marks |
|---|---|
application_number | With this application number (requires office) |
registration_number | With this registration number (requires office) |
ir_number | With this Madrid international registration number |
office | Filed at this one office |
filing_route | Filed this way, such as direct_national, direct_regional, madrid_ir or madrid_designation |
is_madrid | true for Madrid filings |
origin_office_code | With this office of origin (Madrid) |
filing_basis | With these filing bases as filed, e.g. US:1A,US:44E. A basis changed later (an intent-to-use application that filed its statement of use, an added Section 44(e) basis) is not reflected |
register_type | On this register, principal or supplemental (US) |
Other
Other
| Filter | Keeps marks |
|---|---|
has_proceedings | true for marks with at least one proceeding |
seniority_claims | claimed, none or unknown (EUIPO only) |
is_retracted | true for retracted marks |
is_series_mark | true for series marks |
right_kind | Of this right kind, e.g. trademark |
scope_kind | With this territorial scope |
Madrid registrations
A Madrid international registration is one filing with WIPO that covers many countries, each called a designation. New to Madrid? Start with the Madrid Protocol guide.international_registrations sets how search returns them:
grouped(default): one row per registration, with each country’s status undercoverage.expanded: one row per designated country. Use it when you need each country as its own row, for example to filter or count by country.
Example: grouped vs expanded
Example: grouped vs expanded
The same registration, designating the EU, the UK and Japan. Grouped returns one row; expanded repeats the shared fields on a row per country:
{
"id": "tm_7hK2pL9x",
"grain": "mark",
"mark_text": "TIMBER",
"office_code": "WO",
"filing_route": "madrid_ir",
"ir_number": "1939139",
"status": { "primary": "active", "stage": "registered" },
"nice_classes": [9, 42],
"owners": [{ "id": "own_4fQ8tZ2m", "name": "Timber Labs AG", "country_code": "CH" }],
"coverage": {
"territory_count": 3,
"by_primary": { "active": 3 },
"territories": [
{ "territory_code": "EU", "status": { "primary": "active" } },
{ "territory_code": "GB", "status": { "primary": "active" } },
{ "territory_code": "JP", "status": { "primary": "active" } }
]
}
}
[
{
"id": "tm_9nR4sV8w",
"grain": "record",
"mark_text": "TIMBER",
"office_code": "WO",
"jurisdiction_code": "EU",
"filing_route": "madrid_designation",
"ir_number": "1939139",
"status": { "primary": "active", "stage": "registered" },
"nice_classes": [9, 42],
"owners": [{ "id": "own_4fQ8tZ2m", "name": "Timber Labs AG", "country_code": "CH" }]
},
{
"id": "tm_2cX6mT1q",
"grain": "record",
"mark_text": "TIMBER",
"office_code": "WO",
"jurisdiction_code": "GB",
"filing_route": "madrid_designation",
"ir_number": "1939139",
"status": { "primary": "active", "stage": "registered" },
"nice_classes": [9, 42],
"owners": [{ "id": "own_4fQ8tZ2m", "name": "Timber Labs AG", "country_code": "CH" }]
},
{
"id": "tm_5bW8kP3z",
"grain": "record",
"mark_text": "TIMBER",
"office_code": "WO",
"jurisdiction_code": "JP",
"filing_route": "madrid_designation",
"ir_number": "1939139",
"status": { "primary": "active", "stage": "registered" },
"nice_classes": [9, 42],
"owners": [{ "id": "own_4fQ8tZ2m", "name": "Timber Labs AG", "country_code": "CH" }]
}
]
offices with nice_classes. They return expanded rows with an expanded_fallback warning, or a 400 if you set grouped explicitly.
string
default:"grouped"
grouped or expanded.Sorting
Withoutsort, results come back in this order:
- With
q: best match first. - With text filters only: closest text first. Exact matches lead, then marks that start with the value, then shorter marks.
- Filters only: newest filing first.
search_meta.order says which order applied.
string
Up to 3 fields, comma-separated. Prefix with
- for descending, e.g. -filing_date. Available: filing_date, registration_date, expiry_date, renewal_due_date, publication_date, termination_date, updated_at, mark_text, owner_name, jurisdiction_code. On grouped results, filing_date sorts an IR family by the earliest filing date among its records, which can differ from the filing_date its row shows.Count results
aggregations counts the matching records by field, for dashboards and filter sidebars. Each Madrid designation counts as its own record.
Example
Example
curl -G "https://api.signa.so/v1/trademarks" \
-H "Authorization: Bearer $SIGNA_API_KEY" \
--data-urlencode "q=outer banks" \
--data-urlencode "nice_classes=25" \
--data-urlencode "aggregations=office_code,status_stage" \
--data-urlencode "aggregations_only=true"
{
"aggregations": {
"office_code": { "US": 41, "EM": 6, "GB": 3 },
"status_stage": { "registered": 32, "examining": 11, "abandoned": 7 }
}
}
string
Fields to count, comma-separated:
office_code, jurisdiction_code, nice_classes, status_stage, filing_year, mark_feature_type, mark_legal_category, filing_route, right_kind, scope_kind, owner_id, owner_country, entity_id, attorney_id, firm_id. Also owner_count and entity_count for distinct counts.boolean
default:"false"
Return the counts without results.
string
default:"filtered"
Set to
exclude_own_filter for a filter sidebar: each count then ignores its own filter, so with offices=US the office_code counts still show the other offices.Response options
integer
default:"20"
Results per page, from 1 to 100. Page through results with
cursor, as described in Pagination. A search with q reaches at most 10,000 rows; see Search depth.string
Cursor from the previous page.
boolean
default:"false"
Adds
pagination.total_count. Counts stop at 10,000, with total_count_approximate: true when more match.boolean
default:"false"
A search that times out returns a retryable
503. Set this to get the results found so far instead, marked search_meta.complete: false.string
Return only these top-level fields, e.g.
mark_text,status,owners,match. id and object are always kept. The other fields are left out of each row, although the OpenAPI response schema still lists every field.string
full_goods_services returns the full goods and services text instead of the first 280 characters. match_details adds match.details to each result: the matched lanes and the ranking factors applied.POST
POST /v1/trademarks runs the same search with a JSON body, which suits long value lists. The response is the same.
GET | POST |
|---|---|
mark_text_<op> | filters.mark_text.<op> |
mark_text_not_<op> | exclude.mark_text.<op> |
owner_id_not, entity_id_not, entity_group_not, trademark_ids_not | exclude.owner_id, exclude.entity_id, exclude.entity_group, exclude.trademark_ids |
| Other filters | Under filters |
allow_partial, aggregations, aggregations_only, aggregation_mode, include_total | Under options |
q (a string or a list), similarity, territory_match, international_registrations, sort, limit, cursor, fields and include stay at the top level. Lists are JSON arrays, and date ranges become objects: "filing_date": { "gte": "2020-01-01" }.
curl -X POST "https://api.signa.so/v1/trademarks" \
-H "Authorization: Bearer $SIGNA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"q": "nova",
"similarity": ["identical", "fuzzy", "embedded", "phonetic", "lookalike"],
"filters": {
"jurisdictions": ["US", "EU"],
"nice_classes": [9, 25],
"mark_text": { "starts_with": ["nov"] }
},
"exclude": {
"entity_id": ["ent_Wp8qLd4Z"],
"mark_text": { "contains": ["novartis"] }
},
"limit": 20
}'
Response
Each result indata is a trademark with its key facts: mark text, status, office, numbers and dates, classes, owners, and the deadlines Signa computes.
For the full record, pass a result’s id to Retrieve Trademark, or up to 100 IDs to Batch Retrieve. The full record adds attorneys, images, the complete goods and services, per-country Madrid details, related marks and where the data came from.
Search results also include:
number | null
0 to 100, for ordering only. It is not a likelihood of confusion. Set on results found by resemblance to
q, even when sorted, and null for results found only by a trademark number or without q.object
Why the result matched.
Show match fields
Show match fields
string | null
string[]
The channels that found it, or
identifier for a trademark number.string[]
With a list in
q, the names that found it. Otherwise the text filter values it matched.object
Only with
include=match_details: the matched lanes, the ranking factors applied and, with a list in q, each name’s own tier and via in terms.object
On grouped Madrid registrations: each designated territory’s status, plus the designation’s office code, application number and registration number.
object[]
When you filter by
jurisdictions: how the mark covers each requested territory, e.g. an EU trade mark matched for FR.object
How the search ran.
Show search_meta fields
Show search_meta fields
string[]
The channels that ran.
string
The order that applied.
object
How a trademark number in
q was read.boolean
false when allow_partial returned partial results.string
grouped or expanded.object[]
Notes about the results, each with a
code and message, such as expanded_fallback, result_window_reached, all_words_required or multi_word_retry_skipped. Treat the codes as an open list.object
Counts per field, when requested.
Match tiers
match.tier groups results by how closely they match q, which helps you triage a long list. Tiers describe how a mark was found, not a legal conclusion.
All tiers
All tiers
| Tier | For example |
|---|---|
identical | The same text, or the same text in look-alike characters |
near_identical | One changed letter, or a strong sound-alike, in a mark with no more words than q (for a list, than the name that found it). Words are separated by spaces |
similar | Two changed letters, a changed first letter, or q inside a longer mark, including one letter changed or a strong sound-alike there (BRANDX in MARLON BRANDO) |
related | A loose sound-alike, or the words of q spread across a longer mark |
{
"object": "list",
"data": [
{
"id": "tm_8kLm2nPq",
"object": "trademark",
"mark_text": "TIMBER",
"status": { "primary": "active", "stage": "registered" },
"office_code": "US",
"application_number": "97134218",
"filing_date": "2021-11-30",
"nice_classes": [9],
"owners": [{ "id": "own_R3jK9mN2", "name": "Timber Labs, Inc." }],
"relevance_score": 96,
"match": { "tier": "identical", "via": ["identical"], "terms": [] }
},
{
"id": "tm_4hQx7wRt",
"object": "trademark",
"mark_text": "TIMBRE",
"status": { "primary": "pending", "stage": "examining" },
"office_code": "EM",
"application_number": "019295452",
"filing_date": "2026-02-03",
"nice_classes": [9, 42],
"owners": [{ "id": "own_P2mX8vQ1", "name": "Timbre Audio GmbH" }],
"relevance_score": 71,
"match": { "tier": "near_identical", "via": ["fuzzy"], "terms": [] }
}
],
"has_more": true,
"pagination": { "cursor": "eyJpZCI6ImFiYyJ9" },
"search_meta": {
"similarity_applied": ["identical", "fuzzy", "embedded", "lookalike"],
"order": "relevance",
"complete": true,
"international_registrations": "grouped"
},
"request_id": "req_xyz789"
}