Trademark API Reference
A fast JSON REST API for USPTO trademark data — a developer-friendly alternative to scraping TSDR. Search 14M+ records by exact or fuzzy mark literal, look up serial-number status, and filter by owner, class, and date. Bearer-token auth, JSON responses. Get an API key →
Overview
This is the endpoint reference for the Goalie IP USPTO trademark API — structured access to a continuously-refreshed copy of the USPTO trademark dataset, the same data that powers our search tools. Query by mark name, owner, status, international class, serial number, and more. New here? The product overview has a feature summary, TSDR comparison, and pricing.
| Base URL | https://www.goalieip.com |
| Version | v1 |
| Format | JSON request and response bodies |
| Auth | Bearer token in Authorization header |
| Data source | Goalie IP's own refreshed copy of the USPTO trademark dataset |
| Updated | Daily, ingested from USPTO bulk data (occasionally a day behind) |
updatedAt reflects its last refresh. Goalie IP is not affiliated with, or endorsed by, the USPTO.Why this API vs. USPTO TSDR
The USPTO TSDR API returns one record at a time by serial or registration number — there is no way to search by mark text, owner, or class. The Goalie IP API adds full-text, exact, and fuzzy mark search with filtering and pagination across the entire dataset, returning clean JSON built for application use.
| Capability | USPTO TSDR API | Goalie IP API |
|---|---|---|
| Search by mark text (exact / contains / fuzzy) | No — serial/registration lookup only | Yes |
| Filter by owner, class, status, date | No | Yes |
| Serial-number status as JSON | Yes (verbose XML/JSON) | Yes (clean JSON) |
| Pagination over result sets | No | Yes — up to 500 / page |
| Per-call quota usage in response | No | callsUsed / callsRemaining |
| Free tier | Throttled, key required | 200 calls/mo, no card |
Authentication
Every request must include your API key as a Bearer token in the Authorization header. Keys are created and managed in your portal.
Authorization: Bearer gip_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Keys are prefixed with gip_live_ and are 41 characters total. The Bearer prefix is recommended but optional — a bare Authorization: gip_live_… is also accepted, because some directories and clients forward an API-key field verbatim without a scheme. Keep them secret — they are shown only once at creation. You can create up to 5 active keys per account.
www. host directly. Send requests to https://www.goalieip.com/…. The apex goalieip.com redirects to www, and many HTTP clients drop the Authorization header (returning 401) or downgrade POST→GET (returning 405) when following that redirect.Quick start
Search for all active registrations of a mark in a specific class:
curl -X POST https://www.goalieip.com/api/v1/trademarks/search \
-H "Authorization: Bearer gip_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"markLiteral": "ACME",
"markLiteralMode": "exact",
"currentStatusCode": ["700", "800"],
"internationalClasses": ["025"],
"page": 1,
"pageSize": 25
}'Look up a specific trademark by serial number:
curl https://www.goalieip.com/api/v1/trademarks/97123456 \ -H "Authorization: Bearer gip_live_your_key_here"
MCP server (for AI agents)
The same trademark data is available over the Model Context Protocol, so an AI agent can search the US federal register directly — no HTTP client to write and no schema to teach it. See the trademark MCP server overview for how it compares to a TSDR lookup. It is included with every API plan at no extra cost and uses the same key and the same monthly quota as the REST endpoints.
| Endpoint | https://www.goalieip.com/api/mcp |
| Transport | Streamable HTTP |
| Protocol revisions | 2026-07-28 (native) and 2025-11-25 (supported) |
| Authentication | Bearer API key (same as the REST API), or OAuth 2.1 — one-click on claude.ai / ChatGPT |
| Tools | search_trademarks, get_trademark |
Connect on claude.ai or ChatGPT (OAuth — one click)
In claude.ai (or ChatGPT), add a custom connector and paste the server URL https://www.goalieip.com/api/mcp. You'll be sent to GoalieIP to sign in and approve access — no API key to copy and no config file to edit. The connector then searches on your behalf, with usage counting against your account's plan and quota. Revoke access anytime from your portal.
New here? Create a free account first, then click the link in the verification email — an unverified account is refused at the approval step. OAuth connections start on the free tier (200 calls/month) and inherit your plan's limits when you upgrade. For editors and scripts that take a static token, the API-key methods below are simpler.
Claude Code
claude mcp add --transport http goalieip \ https://www.goalieip.com/api/mcp \ --header "Authorization: Bearer gip_live_your_key_here"
Claude API MCP connector (for your own code — not the Claude Desktop app, which is below)
Use this only when your own program calls the Anthropic Messages API and you want Claude to reach our trademark tools while it answers. Anthropic makes the connection to our server for you — there is no config file to edit and nothing to install locally. The request needs three things together: the mcp-client-2025-11-20 beta header, an mcp_servers entry, and a matching mcp_toolset in tools (the API rejects the call if the toolset is missing).
import anthropic
client = anthropic.Anthropic() # your ANTHROPIC_API_KEY
message = client.beta.messages.create(
model="claude-opus-4-5", # or any current Claude model
max_tokens=1024,
betas=["mcp-client-2025-11-20"],
mcp_servers=[
{
"type": "url",
"url": "https://www.goalieip.com/api/mcp",
"name": "goalieip",
"authorization_token": "gip_live_your_key_here",
}
],
tools=[{"type": "mcp_toolset", "mcp_server_name": "goalieip"}],
messages=[
{"role": "user", "content": "Find live trademarks for GOALIE in class 25."}
],
)Two different keys are in play: your Anthropic API key authenticates the call to Anthropic, and your GoalieIP key (gip_live_…) goes in authorization_token so Anthropic can reach our server on your behalf. This is a request from code — not a snippet to paste into any desktop or client config file.
Any MCP client (generic config)
{
"mcpServers": {
"goalieip": {
"type": "http",
"url": "https://www.goalieip.com/api/mcp",
"headers": {
"Authorization": "Bearer gip_live_your_key_here"
}
}
}
}Claude Desktop
Claude Desktop's Add connector screen only accepts OAuth — it has no field for an API key — so a direct URL connection won't work yet. Connect instead through a small local bridge (mcp-remote) that attaches your key for you. It runs automatically; you only edit one config file. Two steps trip most people up, so follow these in order.
1. Find the config file
The reliable way: in Claude Desktop open Settings → Developer → Edit Config. That opens the exact file (creating it if it doesn't exist). To locate it by hand instead:
Windows: %APPDATA%\Claude\claude_desktop_config.json macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
On Windows, paste that path into the File Explorer address bar. Turn on View → File name extensions first, so your editor can't silently save it as .json.txt — Claude only reads the exact .json.
2. Fully quit Claude Desktop before you edit — the step people miss
Closing the window is not enough. Claude Desktop keeps running in the background and rewrites this file with its own copy when it finally exits, erasing your edits. If your changes keep vanishing, this is why. Quit it completely first:
- Windows: right-click the Claude icon in the system tray (bottom-right, possibly under the
^arrow) → Quit. Then open Task Manager (Ctrl+Shift+Esc) and End task on any remaining Claude processes. - macOS: Claude menu → Quit (
⌘Q). Confirm none remain in Activity Monitor.
3. Paste the configuration and save
Replace the file's contents with this, using a key from your portal:
{
"mcpServers": {
"goalieip": {
"command": "npx",
"args": [
"-y", "mcp-remote@latest",
"https://www.goalieip.com/api/mcp",
"--header", "Authorization: Bearer gip_live_your_key_here"
]
}
}
}Works on macOS and Windows as written; the bridge needs Node.js installed. Prefer to keep your key out of the file? Put it in an env block and reference it: add "env": { "GOALIE_KEY": "gip_live_…" } and change the header value to Authorization: Bearer ${GOALIE_KEY}.
npx directly. Wrap it in cmd: set "command": "cmd" and make args begin "/c", "npx", "-y", "mcp-remote@latest", … — the rest is unchanged.4. Reopen Claude Desktop
Start it again and give it a few seconds — the first launch downloads the bridge. goalieip then appears under Settings → Developer, and the two tools are available in a new chat.
Troubleshooting connections
| Symptom | Cause and fix |
|---|---|
406 Not Acceptable | Your client is sending an Accept header that excludes text/event-stream. Clients on protocol revision 2025-11-25 must accept both application/json and text/event-stream. Send Accept: application/json, text/event-stream. A missing header or */* is handled for you. Clients on 2026-07-28 are unaffected — that path ignores Accept entirely. |
401 after a redirect | You configured the apex host. goalieip.com issues a 301 to www, and many clients drop the Authorization header when following it. Always configure https://www.goalieip.com/api/mcp. |
400 on every call | On revision 2026-07-28, requests must carry the Mcp-Method header and self-describe via _meta. Most clients handle this; a hand-rolled one may not. |
Responses look like event: message | That is Server-Sent Events framing, which is correct for revision 2025-11-25 and expected by clients that speak it. The 2026-07-28 path always returns a plain JSON body instead, with no stream parsing required. |
401 from a directory or hosted client | Paste only the key itself into an API-key field. Both Bearer gip_live_… and a bare gip_live_… are accepted, so no scheme prefix is needed. A 401 here means the key is wrong or revoked, not mis-formatted — check it in your portal. |
| Config edits keep vanishing | Claude Desktop only: it rewrites claude_desktop_config.json from its own copy on exit, so edits made while it is running are lost. Fully quit it first — including the system-tray / menu-bar process — then edit. See Claude Desktop above. |
The server answers both protocol revisions on the same URL and selects per request, so there is nothing to configure either way. Verified clients: Claude Code, the Claude API MCP connector, and Claude Desktop (via the bridge above). If your client cannot reach a remote Streamable HTTP server at all, that is a client limitation rather than a server setting — tell us which one and we will test it.
How results differ from the REST API
Tool results are read into a model's context window, so search_trademarks returns a compact summary of each match — serial number, mark, owner, status, filing and registration dates, classes, and the opening of the goods/services text — rather than the full record. Page size defaults to 10 and is capped at 15. Call get_trademark with a serial number for the complete record, including the full goods/services text, owner details, status history, and classifications. The REST API is unchanged and still returns full records at up to 500 per page.
Every search must include at least one narrowing filter — markLiteral, ownerName, serialNumber, registrationNumber, goodsAndServices, or one of the other text filters. Class, status, and date filters alone match too much of the register to run. When a query is rejected or times out, the tool returns a message naming the parameter to change, so an agent can correct itself and retry.
Each tool call counts as one API call against your monthly quota, and the same per-tier burst limits apply. Note that agents typically make several tool calls per question, so MCP usage consumes quota faster than a scripted integration. Coverage is US federal (USPTO) data only — see the FAQ.
Code examples
Common recipes in Python and Node.js. Each call returns JSON; replace gip_live_your_key_here with a key from your portal.
Exact mark query (Python)
import requests
resp = requests.post(
"https://www.goalieip.com/api/v1/trademarks/search",
headers={"Authorization": "Bearer gip_live_your_key_here"},
json={
"markLiteral": "ACME",
"markLiteralMode": "exact",
"currentStatusCode": ["700", "800"], # live / registered
"page": 1,
"pageSize": 25,
},
)
data = resp.json()
print(data["meta"]["total"], "matches")
for tm in data["data"]:
print(tm["serialNumber"], tm["markLiteral"], tm["currentStatusCode"])Serial-number status as JSON (Node.js)
const res = await fetch(
"https://www.goalieip.com/api/v1/trademarks/97123456",
{ headers: { Authorization: "Bearer gip_live_your_key_here" } }
);
const { data } = await res.json();
console.log(data.markLiteral, "→", data.currentStatusCode, data.currentStatusDate);Fuzzy mark-literal search (curl)
Find confusingly similar marks using trigram matching — useful for clearance and watch workflows.
curl -X POST https://www.goalieip.com/api/v1/trademarks/search \
-H "Authorization: Bearer gip_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "markLiteral": "Kodak", "markLiteralMode": "fuzzy", "pageSize": 50 }'Search by owner and class (Python)
resp = requests.post(
"https://www.goalieip.com/api/v1/trademarks/search",
headers={"Authorization": "Bearer gip_live_your_key_here"},
json={
"ownerName": "Nike, Inc.",
"internationalClasses": ["025", "035"],
"filingDateFrom": "2020-01-01",
"sortField": "filingDate",
"sortDir": "desc",
},
)
print(resp.json()["meta"]["total"], "marks")Pseudo-mark search (curl)
The pseudo mark is the USPTO's normalized spelling of a stylized or intentionally misspelled mark — searching it finds phonetic equivalents that a literal search misses (e.g. "KWIK" filings via "QUICK").
curl -X POST https://www.goalieip.com/api/v1/trademarks/search \
-H "Authorization: Bearer gip_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "pseudoMark": "QUICK", "currentStatusCode": ["700", "800"] }'Design search code lookup (curl)
Find marks by their figurative elements using USPTO design search codes (dots optional).
curl -X POST https://www.goalieip.com/api/v1/trademarks/search \
-H "Authorization: Bearer gip_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "designSearchCodes": ["05.03.10"], "internationalClasses": ["025"] }'Full-text goods/services search (curl)
Use goodsAndServicesMode: "words" for whole-word full-text matching with stemming — far faster than contains for common terms, and supports OR and -exclusion. Results come back newest-first.
curl -X POST https://www.goalieip.com/api/v1/trademarks/search \
-H "Authorization: Bearer gip_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "goodsAndServices": "software -mobile", "goodsAndServicesMode": "words" }'POST/api/v1/trademarks/search
Search and filter the trademark database. Returns a paginated list of matching records. Every request must include at least one narrowing filter — one of markLiteral, ownerName, attorneyName, serialNumber, registrationNumber, goodsAndServices, pseudoMark, translation, transliteration, disclaimer, markDescription, or designSearchCodes. All other filters (class, status, dates, correspondence, and administrative fields) narrow results further but cannot be used on their own (a query with none of the above returns 400). Results are sorted by filing date descending unless sortField is set.
Request
POST /api/v1/trademarks/search
Authorization: Bearer gip_live_...
Content-Type: application/json
{
"markLiteral": "NIKE",
"markLiteralMode": "exact",
"ownerName": "Nike, Inc.",
"currentStatusCode": ["700", "800"],
"internationalClasses": ["025", "035"],
"filingDateFrom": "2020-01-01",
"page": 1,
"pageSize": 25
}Response
{
"data": [
{
"serialNumber": "97123456",
"registrationNumber": "6789012",
"markLiteral": "NIKE",
"ownerName": "Nike, Inc.",
"currentStatusCode": "700",
"currentStatusDate": "2022-03-15T00:00:00.000Z",
"filingDate": "2021-06-01T00:00:00.000Z",
"internationalClasses": ["025"],
"goodsAndServices": "Athletic footwear and apparel",
...
}
],
"meta": {
"total": 61,
"totalIsCapped": false,
"page": 1,
"pageSize": 25,
"callsUsed": 1,
"callsRemaining": 4999
}
}meta.total is exact up to 10,000 matches. Broader queries return "total": 10000, "totalIsCapped": true — read it as "10,000+". Pagination past the cap still works: keep requesting pages until one comes back with fewer than pageSize records.
GET/api/v1/trademarks/{serialNumber}
Retrieve a single trademark record by its USPTO serial number. Returns the full record or 404 if not found.
GET /api/v1/trademarks/97123456 Authorization: Bearer gip_live_...
Response
{
"data": {
"serialNumber": "97123456",
"registrationNumber": "6789012",
"markLiteral": "NIKE",
...
},
"meta": {
"callsUsed": 2,
"callsRemaining": 4998
}
}Search parameters
At least one narrowing filter is required (see above); all other fields are optional. Combining multiple fields narrows results (AND logic).
Substring filters (any field described as "substring match") require at least 3 characters — shorter patterns cannot use the search indexes and are rejected with a 400. The one exception is markLiteral with markLiteralMode: "exact", which accepts short marks like "3M".
Mark filters
| Field | Type | Description |
|---|---|---|
markLiteral | string | The text of the mark. Case-insensitive. |
markLiteralMode | "contains" "exact" "fuzzy" | How to match markLiteral. contains — substring match (default). exact — full string equality. fuzzy — phonetic similarity using trigram matching; useful for finding confusingly similar marks. |
markDrawingCode | string | Filter by drawing type code. See Drawing codes. |
Statement filters
| Field | Type | Description |
|---|---|---|
pseudoMark | string | Substring match on the pseudo mark — the USPTO's normalized spelling of a stylized or intentionally misspelled mark (e.g. "QUICK" for the mark "KWIK"). Useful for finding phonetic equivalents. |
translation | string | Substring match on the English translation of foreign wording in the mark. |
transliteration | string | Substring match on the transliteration of non-Latin characters in the mark. |
disclaimer | string | Substring match on disclaimed wording (matter the owner claims no exclusive right to). |
markDescription | string | Substring match on the description of the mark's design elements. |
Owner & attorney filters
| Field | Type | Description |
|---|---|---|
ownerName | string | Substring match on the registrant/owner name. |
attorneyName | string | Substring match on the attorney of record. |
attorneyDocketNumber | string | Substring match on the attorney's internal docket reference. |
correspondentAddress | string | Substring match on the mailing address of the correspondent of record. |
domesticRepresentativeName | string | Substring match on the domestic representative (US contact for foreign applicants). |
Classification & goods filters
| Field | Type | Description |
|---|---|---|
internationalClasses | string[] | Filter by Nice Classification codes. Pass multiple to match any (OR logic). Values are zero-padded three-digit strings, e.g. "025", "035". See the 45 trademark classes. |
goodsAndServices | string | Search the goods and services description. See goodsAndServicesMode for match behavior. |
goodsAndServicesMode | "contains" "words" | How to match goodsAndServices. contains — substring match anywhere, including inside words (default). words — full-text whole-word match with English stemming ("shoes" matches shoe), supporting OR and -exclusion. Use words for a common single term (e.g. "software", "footwear"): it is far faster than contains, which times out on very common terms. Results default to newest-first. Multiple words are matched together (AND); a query combining only very common words can still time out (a 504) — prefer one distinctive term. Words mode matches whole words only, so "tooth" will not match toothbrush. |
designSearchCodes | string[] | Filter by USPTO design search codes for figurative elements. Pass multiple to match any (OR logic). Codes are 6 digits — category, division, section — with or without dots: "050310" and "05.03.10" are equivalent. |
Status & identifier filters
| Field | Type | Description |
|---|---|---|
serialNumber | string | Exact match on the USPTO serial number. |
registrationNumber | string | Exact match on the registration number. |
currentStatusCode | string[] | Filter by one or more status codes (OR logic). E.g. ["700", "800"] for all registered marks. See Status codes. |
cancellationCode | string | Exact match on the cancellation code, for cancelled registrations. |
Administrative filters
| Field | Type | Description |
|---|---|---|
employeeName | string | Substring match on the USPTO examining attorney assigned to the application. |
currentLocation | string | Substring match on the file's current physical location at the USPTO (e.g. "PUBLICATION AND ISSUE SECTION"). |
lawOfficeAssignedLocationCode | string | Exact match on the assigned USPTO law office code (e.g. "L70"). |
Date range filters
| Field | Type | Description |
|---|---|---|
filingDateFrom | ISO 8601 date string | Include only records filed on or after this date. Example: "2023-01-01". |
filingDateTo | ISO 8601 date string | Include only records filed on or before this date. Example: "2023-01-01". |
currentStatusDateFrom | ISO 8601 date string | Filter by status change date (on or after). Example: "2023-01-01". |
currentStatusDateTo | ISO 8601 date string | Filter by status change date (on or before). Example: "2023-01-01". |
firstUseDateFrom | ISO 8601 date string | Filter by claimed date of first use anywhere (on or after). Example: "2023-01-01". |
firstUseDateTo | ISO 8601 date string | Filter by claimed date of first use anywhere (on or before). Example: "2023-01-01". |
firstCommercialUseDateFrom | ISO 8601 date string | Filter by claimed date of first use in commerce (on or after). Example: "2023-01-01". |
firstCommercialUseDateTo | ISO 8601 date string | Filter by claimed date of first use in commerce (on or before). Example: "2023-01-01". |
registrationDateFrom | ISO 8601 date string | Filter by registration date (on or after). Example: "2023-01-01". |
registrationDateTo | ISO 8601 date string | Filter by registration date (on or before). Example: "2023-01-01". |
abandonmentDateFrom | ISO 8601 date string | Filter by abandonment date (on or after). Example: "2023-01-01". |
abandonmentDateTo | ISO 8601 date string | Filter by abandonment date (on or before). Example: "2023-01-01". |
renewalDateFrom | ISO 8601 date string | Filter by renewal date (on or after). Example: "2023-01-01". |
renewalDateTo | ISO 8601 date string | Filter by renewal date (on or before). Example: "2023-01-01". |
transactionDateFrom | ISO 8601 date string | Filter by the most recent USPTO transaction date (on or after). Example: "2023-01-01". |
transactionDateTo | ISO 8601 date string | Filter by the most recent USPTO transaction date (on or before). Example: "2023-01-01". |
publishedForOppositionDateFrom | ISO 8601 date string | Filter by Official Gazette publication date (on or after). Example: "2023-01-01". |
publishedForOppositionDateTo | ISO 8601 date string | Filter by Official Gazette publication date (on or before). Example: "2023-01-01". |
amendToRegisterDateFrom | ISO 8601 date string | Filter by amend-to-register date (on or after). Example: "2023-01-01". |
amendToRegisterDateTo | ISO 8601 date string | Filter by amend-to-register date (on or before). Example: "2023-01-01". |
cancellationDateFrom | ISO 8601 date string | Filter by cancellation date (on or after). Example: "2023-01-01". |
cancellationDateTo | ISO 8601 date string | Filter by cancellation date (on or before). Example: "2023-01-01". |
republished12cDateFrom | ISO 8601 date string | Filter by §12(c) republication date (on or after). Example: "2023-01-01". |
republished12cDateTo | ISO 8601 date string | Filter by §12(c) republication date (on or before). Example: "2023-01-01". |
locationDateFrom | ISO 8601 date string | Filter by the date the file reached its current USPTO location (on or after). Example: "2023-01-01". |
locationDateTo | ISO 8601 date string | Filter by the date the file reached its current USPTO location (on or before). Example: "2023-01-01". |
createdAtFrom | ISO 8601 date string | Filter by when the record was added to the Goalie IP database (on or after). Example: "2023-01-01". |
createdAtTo | ISO 8601 date string | Filter by when the record was added to the Goalie IP database (on or before). Example: "2023-01-01". |
updatedAtFrom | ISO 8601 date string | Filter by when the record was last updated (on or after). Useful for syncing only recently changed records. Example: "2023-01-01". |
updatedAtTo | ISO 8601 date string | Filter by when the record was last updated (on or before). Example: "2023-01-01". |
Pagination & sorting
| Field | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number, 1-indexed. |
pageSize | integer | 25 | Results per page. Max 500. |
sortField | string | filingDate | Field to sort by. One of: serialNumber, registrationNumber, filingDate, transactionDate, markLiteral, markDrawingCode, attorneyName, ownerName, goodsAndServices, currentStatusCode, currentStatusDate, publishedForOppositionDate, firstUseDate, firstCommercialUseDate, registrationDate, abandonmentDate, renewalDate, createdAt, updatedAt. |
sortDir | "asc" "desc" | "asc" | Sort direction. |
Response fields
Each record in data[] contains the following fields.
| Field | Type | Description |
|---|---|---|
serialNumber | string | USPTO serial number. Unique identifier for each application. |
registrationNumber | string | null | Registration number once granted. "0000000" means not yet registered. |
markLiteral | string | null | The text of the mark. Null for purely design marks. |
markDrawingCode | string | Code indicating the type of mark drawing. See Drawing codes. |
ownerName | string | null | Name of the current owner/registrant. |
ownerAddress | object | null | Structured address: city, country, postcode, address_1, nationality_country, legal_entity_type_code. |
correspondentAddress | string | null | Full mailing address of the correspondent of record. |
attorneyName | string | null | Attorney of record at the USPTO. |
attorneyDocketNumber | string | null | Attorney's internal docket reference, if recorded. |
domesticRepresentativeName | string | null | Domestic representative (US contact for foreign applicants). |
filingDate | ISO datetime | null | Date the application was filed. |
transactionDate | ISO datetime | Date of the most recent USPTO transaction. |
currentStatusCode | string | Current application/registration status code. See Status codes. |
currentStatusDate | ISO datetime | Date the current status was set. |
statusHistory | array | Ordered list of all status events: { code, date, description }. |
publishedForOppositionDate | ISO datetime | null | Date published for opposition in the Official Gazette, if applicable. |
firstUseDate | ISO datetime | null | Claimed date of first use anywhere. |
firstCommercialUseDate | ISO datetime | null | Claimed date of first use in commerce. |
registrationDate | ISO datetime | null | Date the mark registered, if applicable. |
amendToRegisterDate | ISO datetime | null | Date of amendment to the register, if applicable. |
abandonmentDate | ISO datetime | null | Date the application was abandoned, if applicable. |
cancellationCode | string | null | Cancellation code, for cancelled registrations. |
cancellationDate | ISO datetime | null | Date the registration was cancelled, if applicable. |
republished12cDate | ISO datetime | null | Date republished under §12(c), if applicable. |
renewalDate | ISO datetime | null | Date the registration was last renewed, if applicable. |
internationalClasses | string[] | Nice Classification class codes, e.g. ["025", "035"]. Empty array if unclassified. |
goodsAndServices | string | null | Full goods and services description from the application. |
markDescription | string | null | Description of the mark's design elements. |
disclaimer | string | null | Wording the owner claims no exclusive right to. |
pseudoMark | string | null | USPTO-normalized spelling of a stylized or misspelled mark. |
translation | string | null | English translation of foreign wording in the mark. |
transliteration | string | null | Transliteration of non-Latin characters in the mark. |
designSearchCodes | string[] | USPTO design search codes for figurative elements, e.g. ["050310"]. Empty array for word marks. |
owners | array | null | All owner records with legal entity details. |
classifications | array | null | Full classification records including US class codes. |
statementsCategorized | object | null | All application statements grouped by type. |
priorRegistrations | array | null | Claimed prior registrations. |
foreignApplications | array | null | Foreign applications claimed as priority basis. |
internationalRegistration | object | null | Madrid Protocol international registration details, if any. |
madridFilings | array | null | Madrid Protocol filing history, if any. |
currentLocation | string | null | Current physical location at the USPTO (e.g. "PUBLICATION AND ISSUE SECTION"). |
lawOfficeAssignedLocationCode | string | null | Assigned USPTO law office code. |
locationDate | ISO datetime | null | Date the file reached its current USPTO location. |
employeeName | string | null | USPTO examining attorney assigned to the application. |
headerFlags | object | Boolean flags for application attributes (see below). |
createdAt | ISO datetime | When this record was added to the Goalie IP database. |
updatedAt | ISO datetime | When this record was last updated. |
headerFlags
Boolean attributes of the application. Commonly useful flags:
| Flag | Meaning when true |
|---|---|
trademark_in | Filed as a trademark (vs. service mark, collective mark, etc.) |
service_mark_in | Filed as a service mark |
intent_to_use_in | Original filing basis was intent-to-use (§1(b)) |
use_application_currently_in | Current basis is use in commerce (§1(a)) |
standard_characters_claimed_in | Mark is in standard characters with no design claim |
color_drawing_current_in | Color is currently claimed as a feature of the mark |
section_8_filed_in | Section 8 declaration of use has been filed |
section_15_filed_in | Section 15 declaration of incontestability has been filed |
renewal_filed_in | Renewal application has been filed |
opposition_pending_in | Opposition proceeding is currently pending |
cancellation_pending_in | Cancellation proceeding is currently pending |
foreign_priority_in | Foreign priority claim under §44(d) |
meta
| Field | Description |
|---|---|
total | Total number of records matching the query (across all pages). |
page | Current page number. |
pageSize | Number of results returned on this page. |
callsUsed | Total API calls made by your account this calendar month, including this request. |
callsRemaining | Calls remaining this month before your quota is exhausted. |
Status codes
The currentStatusCode field uses USPTO internal codes. The most commonly encountered codes by category:
| Code | Description |
|---|---|
630 | New application — not yet assigned to examiner |
638 | New application — assigned to examiner |
641 | Non-final office action mailed |
645 | Final refusal mailed |
680 | Approved for publication |
686 | Published for opposition |
688 | Notice of allowance issued |
718–734 | Statement of use extension requests (ITU) |
744 | Statement of use filed |
760 | Ex parte appeal pending |
774 | Opposition pending |
| Code | Description |
|---|---|
700 | Registered |
701 | Section 8 accepted (declaration of use filed) |
702 | Section 8 & 15 accepted — incontestable |
703 | Section 15 acknowledged |
800 | Registered and renewed |
| Code | Description |
|---|---|
600 | Abandoned — incomplete response |
601 | Abandoned — express |
602 | Abandoned — failure to respond or late response |
606 | Abandoned — no statement of use filed |
900 | Expired |
| Code | Description |
|---|---|
709 | Cancelled — Section 71 |
710 | Cancelled — Section 8 (failure to file maintenance) |
711 | Cancelled — Section 7 |
712 | Cancelled by court order (Section 37) |
626 | Registered backfile — cancelled or expired |
To filter for all live registered marks, pass "currentStatusCode": ["700", "701", "702", "703", "704", "705", "800"].
Drawing codes
The markDrawingCode field indicates the form of the mark drawing submitted to the USPTO.
| Code | Description |
|---|---|
1 | Typed drawing — plain word mark, no design element |
2 | Design without color claim |
3 | Design with color claim |
4 | Standard character mark — word in any font, size, or color |
5 | Standard character mark with color claim |
6 | Special form — word combined with design element |
To find only word marks (no design element), filter for markDrawingCode: "4" (standard characters) or "1" (typed drawing).
Error responses
Errors return a JSON body with an error string and the appropriate HTTP status code.
{ "error": "Monthly call quota of 5000 calls exceeded. Upgrade at https://goalieip.com/subscribe" }| Status | Cause | Resolution |
|---|---|---|
400 | Invalid JSON, an unrecognized parameter, a malformed date, a substring filter under 3 characters, or no narrowing filter supplied. | Check the error message for the specific field. Include at least one narrowing filter (see POST /search). |
401 | Missing, malformed, or revoked API key. | Verify the Authorization header is present and the key is active in your portal. Call the www. host directly. |
404 | Serial number not found (lookup endpoint only). | Verify the serial number. USPTO serial numbers are 8 digits. |
429 | Monthly call quota exceeded, or the per-key burst limit was hit. | For quota: upgrade or wait for the 1st of next month. For a burst: honor the Retry-After header and slow down. |
500 | Internal server error. | Retry the request. If it persists, contact reid@goalieip.com. |
503 | Search is momentarily busy (concurrency limit) or the database is warming up. | Retryable — wait for the Retry-After delay and try again. |
504 | The query exceeded the server time limit (typically a broad fuzzy search). | Narrow the query (more specific term, add a filter, or use exact/contains), then retry. |
Rate limits
Two limits apply. The monthly quota is enforced at the account level — all API keys on the same account share one monthly pool, and MCP tool calls draw from that same pool as REST requests. It resets on the 1st of each calendar month. A separate short-window burst limit caps how many requests a single key may make in a rolling 10-second window, protecting shared capacity; the REST endpoints and the MCP server each apply that ceiling independently, and paid tiers are far more generous.
| Plan | Calls / month | Burst limit | Overage |
|---|---|---|---|
| Free | 200 | 10 / 10s | None — requests return 429 when exhausted |
| API Starter ($19/mo) | 5,000 | 50 / 10s | +$0.0040 per call |
| API Professional ($49/mo) | 30,000 | 100 / 10s | +$0.0020 per call |
| API Business ($149/mo) | 150,000 | 150 / 10s | +$0.0015 per call |
Exceeding the burst limit returns 429 with a Retry-After header (seconds) plus X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. Back off and retry after the indicated delay. Burst-limited requests do not count against your monthly quota.
How overage works
Going past your included calls does not switch your API off. On a paid plan, additional calls keep working and are billed at your plan's overage rate, charged to the card on file once the billing period closes — separately from your subscription. You'll get an email receipt itemising the calls and the amount.
We email you at 80% of your allowance and again when you reach it, so overage is never a surprise. If you are regularly exceeding your plan, the next tier up costs less than the overage — that is deliberate, and the notices say so.
Overage is capped at 3× your plan's included calls, after which requests return 429. The cap exists so a misconfigured client or a looping agent cannot run up an unbounded charge; if you need it raised, contact us and we will lift it or move you to a plan that fits.
The free tier does not have overage. It stops at 200 calls and resumes at the start of the next month.
Every response includes meta.callsUsed and meta.callsRemaining so you can monitor consumption in your application. Upgrade your plan →
Frequently asked questions
Is there a USPTO trademark search API?
Yes. The Goalie IP Trademark Search API is a JSON REST API over 14M+ USPTO trademark records. Send a POST to /api/v1/trademarks/search with a Bearer token to search by mark literal, pseudo mark, translation, owner, attorney, design search code, goods and services (substring or full-text word match), status, international class, and filing date, or GET /api/v1/trademarks/{serialNumber} to look up a single record.
How do I get a trademark's status as JSON by serial number?
Call GET https://www.goalieip.com/api/v1/trademarks/{serialNumber} with your API key in the Authorization header. The response is JSON including currentStatusCode and currentStatusDate, so you can read the current USPTO status of any serial number programmatically.
Can I do an exact mark-literal search through the API?
Yes. POST to /api/v1/trademarks/search with {"markLiteral":"ACME","markLiteralMode":"exact"} for a full-string match. Use "contains" for a substring search or "fuzzy" for trigram-based similarity matching of confusingly similar marks.
How is this different from the USPTO TSDR API?
USPTO TSDR returns one record at a time by serial or registration number and is rate-limited and verbose. The Goalie IP API adds full-text and fuzzy mark search, owner/class/date filtering, and pagination across the whole dataset, returning clean JSON built for application use.
Does the trademark API return data in JSON?
Yes. Both endpoints speak JSON. A search POST returns a data array of trademark records plus a meta object (total, page, pageSize, callsUsed, callsRemaining); the serial-number lookup returns a single record as JSON. You send JSON search filters in the request body with a Bearer token and get JSON back — no XML, no scraping.
Is the trademark search API free?
There is a free tier of 200 calls per month with no credit card required. Paid plans are $19/month for 5,000 calls (Starter), $49/month for 30,000 calls (Professional), and $149/month for 150,000 calls (Business).
Is the data live from the USPTO?
No. The API serves Goalie IP's own copy of the USPTO trademark dataset, ingested from USPTO bulk data and refreshed daily (occasionally a day behind) — not a live connection to USPTO systems. Each record's updatedAt field reflects its last refresh. Goalie IP is not affiliated with or endorsed by the USPTO.
Ready to build? View API plans and get a key →