EUIPO¶
Read-only access to the EUIPO Trademark Search and Design Search APIs — all EU trademarks (~2.3M EUTMs + international registrations designating the EU, since 1996) and all Registered Community Designs (~1.5M RCDs, since 2003).
Auth¶
OAuth 2.0 client_credentials with scope uid. Both APIs share the same dev-portal app. Two-step setup:
- Register an App. Sign up at dev.euipo.europa.eu (production) and/or dev-sandbox.euipo.europa.eu (sandbox — separate auth realm). Each app gives you a
client_id(shown as "API Key" in the UI; sent inX-IBM-Client-Id) and aclient_secret. - Subscribe. From the App's Subscriptions tab, subscribe to the "Trademark Search" and "Design Search" products on the Default Plan. Sandbox subscriptions activate immediately. Production subscriptions require sending identity documents to
docs.apiplatform@euipo.europa.eu(passport copy for natural persons; company register excerpt for legal persons).
Set EUIPO_CLIENT_ID and EUIPO_CLIENT_SECRET in your environment. Optional: EUIPO_ENV=sandbox to point the clients at the sandbox host.
import os
os.environ["EUIPO_CLIENT_ID"] = "..."
os.environ["EUIPO_CLIENT_SECRET"] = "..."
os.environ["EUIPO_ENV"] = "sandbox" # default: production
Sandbox data caveat. The sandbox is a frozen historical snapshot plus synthetic test rows (newest TMs are labelled "EUTM Generated by QC Automated Script"). API response shapes are identical to production — useful for development and tests — but it cannot answer freshness questions like "what was filed last week."
Quick Start¶
Trademarks¶
from patent_client_agents.euipo_trademarks import (
EuipoTrademarksClient,
SearchTrademarksInput,
GetTrademarkInput,
search_trademarks,
get_trademark,
)
# One-shot search
page = await search_trademarks(SearchTrademarksInput(
query="wordMarkSpecification.verbalElement==*Apple* and status==REGISTERED",
size=25,
sort="applicationDate:desc",
))
print(f"{page.total_elements} matches across {page.total_pages} pages")
# Detail by application number
tm = await get_trademark(GetTrademarkInput(application_number="000428557"))
print(tm.status, tm.application_language)
for gns in tm.goods_and_services:
print(gns.class_number, gns.terms_in("en"))
# Batch — one client, one token, one cache
async with EuipoTrademarksClient(environment="sandbox") as client:
for tm_row in page.trademarks:
full = await client.get_trademark(tm_row.application_number)
...
Designs¶
from patent_client_agents.euipo_designs import (
SearchDesignsInput,
GetDesignInput,
search_designs,
get_design,
)
# Locarno class 14 (recording / communication equipment), 2024 onwards
page = await search_designs(SearchDesignsInput(
query="applicationDate>=2024-01-01 and locarnoClasses=in=(14.01,14.02,14.03)",
sort="applicationDate:desc",
))
# Detail by design number (NNNNNNNNN-NNNN)
d = await get_design(GetDesignInput(design_number="099037115-0001"))
print(d.product_terms_in("en"))
for v in d.views:
print(v.order, v.image_format)
Query Language — RSQL¶
EUIPO's query parameter is RSQL (FIQL variant). Operators:
| Operator | Meaning | Example |
|---|---|---|
== |
Exact | markFeature==FIGURATIVE |
!= |
Not-equal | status!=EXPIRED |
=in= |
Set membership | niceClasses=in=(25,35) |
=out= |
Set non-membership | niceClasses=out=(40) |
=all= |
All-of | niceClasses=all=(25,28,40) |
< / > / <= / >= |
Ordered comparisons | applicationDate>=2024-01-01 |
* |
String wildcard | applicants.name==*Apple* |
and / or / not |
Booleans (with parens) | (markFeature==WORD and niceClasses=all=(25)) |
Field paths are dotted (e.g. wordMarkSpecification.verbalElement).
Endpoints¶
Trademarks¶
| Method | Path | Coverage |
|---|---|---|
| GET | /trademarks |
RSQL search |
| GET | /trademarks/{applicationNumber} |
Full record (~40 fields) |
| GET | /trademarks/{applicationNumber}/image |
Mark image (figurative / 3D / colour / position / pattern) |
| GET | /trademarks/{applicationNumber}/image/thumbnail |
Thumbnail |
| GET | /trademarks/{applicationNumber}/sound |
Sound mark audio |
| GET | /trademarks/{applicationNumber}/video |
Multimedia / motion mark video |
| GET | /trademarks/{applicationNumber}/model |
3D shape mark model |
Designs¶
| Method | Path | Coverage |
|---|---|---|
| GET | /designs |
RSQL search |
| GET | /designs/{designNumber} |
Full record |
| GET | /designs/{designNumber}/views/{order} |
Image of one view (1-indexed angle) |
| GET | /designs/{designNumber}/views/{order}/thumbnail |
View thumbnail |
| GET | /designs/{designNumber}/model |
3D model |
Functions¶
Trademarks¶
| Function | Description |
|---|---|
search_trademarks(SearchTrademarksInput) |
RSQL search |
get_trademark(GetTrademarkInput) |
Full record |
get_trademark_image(GetTrademarkMediaInput) |
Mark image bytes |
get_trademark_image_thumbnail(GetTrademarkMediaInput) |
Thumbnail bytes |
get_trademark_sound(GetTrademarkMediaInput) |
Sound bytes |
get_trademark_video(GetTrademarkMediaInput) |
Video bytes |
get_trademark_model(GetTrademarkMediaInput) |
3D model bytes |
Designs¶
| Function | Description |
|---|---|
search_designs(SearchDesignsInput) |
RSQL search |
get_design(GetDesignInput) |
Full record |
get_design_view(GetDesignViewInput) |
View image bytes |
get_design_view_thumbnail(GetDesignViewInput) |
View thumbnail bytes |
get_design_model(GetDesignMediaInput) |
3D model bytes |
Limits and ToS¶
- HTTPS only
- 25,000 calls/day per app on the Default Plan
- Page size: 10..100 (HTTP 400 for
size<10) - Access token lifetime: 7,200 s — refreshed automatically by the library on 401
- Both
Authorization: Bearer ...andX-IBM-Client-Id: ...are required on every request - Subscriptions are per-product; production access gated on identity-document review
MCP Tool Surface¶
MCP tools register only when both EUIPO_CLIENT_ID and EUIPO_CLIENT_SECRET are set (matches the JPO / CanLII env-gating pattern). Four tools cover the read path:
search_euipo_trademarks, get_euipo_trademark, search_euipo_designs, get_euipo_design.
Media downloads (images, sound, video, 3D models) are library-only in v1.