Skip to content

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:

  1. 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 in X-IBM-Client-Id) and a client_secret.
  2. 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 ... and X-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.