Pinakel · Trademark Analytics API · v1
Integrator Guide
Aggregate trademark analytics — filing trends, Nice-class distribution, owner and representative leaderboards — computed over our worldwide corpus and served as JSON, so you can build your own dashboards without running a warehouse. Base URL:
https://api.pinakelai.com/analytics
Interactive OpenAPI reference: https://api.pinakelai.com/analytics/docs · machine-readable spec: /openapi.json
Also on this host: /data — per-record trademark search, lookup and change tracking, sold and keyed separately.
Authentication
Every request carries your API key in the X-API-Key header
(or as a Bearer token). Keys are scoped per
product: a key sold for analytics returns
403 product_not_enabled on the record API, and vice versa. That
is a provisioning state, not a bad credential — do not rotate the
key; ask us to enable the product.
curl -H "X-API-Key: pk_live_…" \
"https://api.pinakelai.com/analytics/v1/kpis?jurisdiction=US"
Coverage: read this before you render a number
The corpus is being loaded register by register. Every response
carries a coverage block describing exactly what the
figures in it were computed over:
{
"kpis": { … },
"coverage": {
"jurisdiction": "US",
"rows_loaded": 200000,
"backfill_status": "in_progress",
"is_sample": true,
"last_ingested_date": "2026-09-14"
}
}
is_sample is true the figures are
indicative, not exhaustive — that register is a partial load, so
counts are floors and rankings can move as it completes. Surface this in your
UI. It is the one error your users cannot detect for themselves, and the only
thing we ask of anyone reselling these numbers.Live from the warehouse — 119 registers, 4,514,223 records, 119 still sampling. This table is generated per-request and never overstates.
| register | records | backfill | basis |
|---|---|---|---|
| AD | 49,134 | in_progress | sample |
| AE | 0 | not_started | sample |
| AL | 26,801 | in_progress | sample |
| AM | 47,582 | in_progress | sample |
| AP | 8,335 | in_progress | sample |
| AR | 50,000 | in_progress | sample |
| AT | 50,000 | in_progress | sample |
| AU | 50,000 | in_progress | sample |
| AZ | 0 | not_started | sample |
| BA | 31,003 | in_progress | sample |
| BD | 0 | not_started | sample |
| BG | 50,000 | in_progress | sample |
| BH | 50,000 | in_progress | sample |
| BN | 50,000 | in_progress | sample |
| BO | 0 | not_started | sample |
| BQ | 7,727 | in_progress | sample |
| BR | 50,000 | in_progress | sample |
| BT | 24,447 | in_progress | sample |
| BX | 50,000 | in_progress | sample |
| BY | 50,000 | in_progress | sample |
| BZ | 0 | not_started | sample |
| CA | 50,000 | in_progress | sample |
| CH | 50,000 | in_progress | sample |
| CL | 50,000 | in_progress | sample |
| CN | 50,000 | in_progress | sample |
| CO | 50,000 | in_progress | sample |
| CR | 50,000 | in_progress | sample |
| CU | 50,000 | in_progress | sample |
| CY | 50,000 | in_progress | sample |
| CZ | 50,000 | in_progress | sample |
| DE | 50,000 | in_progress | sample |
| DK | 50,000 | in_progress | sample |
| DO | 0 | not_started | sample |
| DZ | 50,000 | in_progress | sample |
| EC | 50,000 | in_progress | sample |
| EE | 50,000 | in_progress | sample |
| EG | 0 | not_started | sample |
| EM | 50,000 | in_progress | sample |
| ES | 50,000 | in_progress | sample |
| FI | 50,000 | in_progress | sample |
| FR | 50,000 | in_progress | sample |
| GB | 100,000 | in_progress | sample |
| GE | 50,000 | in_progress | sample |
| GG | 0 | not_started | sample |
| GR | 0 | not_started | sample |
| GT | 0 | not_started | sample |
| HK | 50,000 | in_progress | sample |
| HR | 50,000 | in_progress | sample |
| HU | 50,000 | in_progress | sample |
| ID | 50,000 | in_progress | sample |
| IE | 50,000 | in_progress | sample |
| IL | 50,000 | in_progress | sample |
| IN | 50,000 | in_progress | sample |
| IS | 50,000 | in_progress | sample |
| IT | 100,000 | in_progress | sample |
| JE | 0 | not_started | sample |
| JO | 50,000 | in_progress | sample |
| JP | 100,000 | in_progress | sample |
| KE | 0 | not_started | sample |
| KG | 0 | not_started | sample |
| KH | 50,000 | in_progress | sample |
| KR | 50,000 | in_progress | sample |
| KW | 0 | not_started | sample |
| KZ | 0 | not_started | sample |
| LA | 50,000 | in_progress | sample |
| LI | 0 | not_started | sample |
| LK | 0 | not_started | sample |
| LT | 50,000 | in_progress | sample |
| LV | 50,000 | in_progress | sample |
| LY | 28,552 | in_progress | sample |
| MC | 0 | not_started | sample |
| MD | 50,000 | in_progress | sample |
| ME | 19,645 | in_progress | sample |
| MK | 42,852 | in_progress | sample |
| MM | 20,213 | in_progress | sample |
| MN | 50,000 | in_progress | sample |
| MO | 50,000 | in_progress | sample |
| MT | 0 | not_started | sample |
| MX | 50,000 | in_progress | sample |
| MY | 50,000 | in_progress | sample |
| NG | 0 | not_started | sample |
| NI | 0 | not_started | sample |
| NO | 50,000 | in_progress | sample |
| NZ | 50,000 | in_progress | sample |
| OA | 50,000 | in_progress | sample |
| PA | 50,000 | in_progress | sample |
| PE | 0 | not_started | sample |
| PG | 38,465 | in_progress | sample |
| PH | 50,000 | in_progress | sample |
| PL | 50,000 | in_progress | sample |
| PR | 0 | not_started | sample |
| PT | 50,000 | in_progress | sample |
| RO | 50,000 | in_progress | sample |
| RS | 0 | not_started | sample |
| RU | 50,000 | in_progress | sample |
| SA | 50,000 | in_progress | sample |
| SD | 44,463 | in_progress | sample |
| SE | 50,000 | in_progress | sample |
| SG | 50,000 | in_progress | sample |
| SI | 50,000 | in_progress | sample |
| SK | 50,000 | in_progress | sample |
| SM | 4,746 | in_progress | sample |
| SV | 0 | not_started | sample |
| SX | 14,751 | in_progress | sample |
| TH | 50,000 | in_progress | sample |
| TR | 50,000 | in_progress | sample |
| TT | 50,000 | in_progress | sample |
| TW | 100,000 | in_progress | sample |
| UA | 50,000 | in_progress | sample |
| UG | 50,000 | in_progress | sample |
| US | 200,000 | in_progress | sample |
| UY | 50,000 | in_progress | sample |
| UZ | 0 | not_started | sample |
| VN | 50,000 | in_progress | sample |
| WO | 50,000 | in_progress | sample |
| WS | 10,756 | in_progress | sample |
| XK | 44,751 | in_progress | sample |
| ZA | 0 | not_started | sample |
| ZM | 0 | not_started | sample |
Pagination
List endpoints return {"items": […], "next_cursor": "…"|null,
"has_more": true|false}. Pass next_cursor back as
cursor for the next page; null means you have
reached the end. Cursors are bound to the exact query that produced them, so
changing a filter mid-pagination is refused rather than silently mixing
result sets — start a new scan instead.
There is deliberately no total-row count. Use
has_more to drive "next page", and /v1/search/facets
when you need counts: it reports exact totals per status, jurisdiction, year,
class and owner for the same filters.
Aggregate endpoints
These take an optional jurisdiction (a 2–4 letter
register code, e.g. US); omit it for every register combined.
Leaderboards take limit (1–500). Responses are an envelope:
{"items": […], "coverage": {…}}.
GET/v1/jurisdictions
Every register we hold analytics for, with counts and year range. The machine-readable coverage table — poll it to discover what is available as the corpus loads.
GET/v1/kpis
Headline totals for a register or the whole corpus: records, live vs dead, distinct owners, registration rate.
GET/v1/filings-by-year
Filing volume per year, split live vs dead. Optional
year_from / year_to (1800–2100).
GET/v1/nice-classes
Distribution of filings across the 45 Nice classes.
GET/v1/top-owners
Largest filers by volume, with their live ratio. limit
defaults to 50.
GET/v1/law-firms
Representative leaderboard: volume, success rate, median days to
registration, distinct clients. limit defaults to 100.
GET/v1/squatting
Owners matching the squatting heuristic — high volume, low registration rate, filings scattered across unrelated classes. A signal to investigate, not a determination.
GET/v1/collateral
Owners with security interests recorded against their marks.
GET/v1/search
Structured search across the corpus. q plus
search_in (name, owner,
rep, keyword, all),
include_translations, and filters for
owner_key, nice_class, status,
jurisdiction, year_from/year_to.
Keyset pagination via cursor.
jurisdiction or
year_from/year_to filter prunes it
5–25×, because the warehouse is partitioned by
filing year and clustered on register. Use them whenever your case allows.GET/v1/search/facets
Breakdowns over the same filters as /v1/search: status,
jurisdiction, year, Nice class and top owners.
GET/v1/renewals
Upcoming renewal deadlines, soonest first.
months_ahead (1–120), owner_key,
nice_class, jurisdiction. Served from a pre-built
aggregate, so it is far cheaper than the equivalent search.
GET/v1/dead-marks
Marks the register no longer treats as live. Same grammar as
/v1/search, filtered to dead.
GET/v1/owners/{owner_key}
One owner's portfolio. ?include=filings_by_year,jurisdictions,status
adds breakdowns.
GET/v1/brands/{brand_key}
One brand name across registers and owners.
?include=owners adds the owner list.
owner_key and brand_key
are opaque. Take them from a search hit or a facet and pass them
back verbatim — do not construct or parse them. They are normalized
forms produced by our pipeline, and treating them as opaque is what lets us
improve that normalization without breaking your integration.GET/v1/usage
Your key's own month-to-date call count, quota and rate limit, broken down per product, so you can watch consumption without asking us.
Errors & limits
Every non-2xx response carries one envelope:
{"error": {"code": "…", "message": "…", "param": "…"}} —
code is the machine-readable contract, param names
the offending parameter when one exists.
| status | code | meaning |
|---|---|---|
| 400 | invalid_parameter | malformed request — the message says exactly what to fix |
| 401 | unauthorized | missing or unknown API key |
| 403 | key_revoked | revoked key |
| 403 | product_not_enabled | valid key, but it was not sold analytics — do not rotate the key, ask us to enable it |
| 404 | not_found | no such path — check the /analytics prefix |
| 422 | query_too_broad | the query would scan more data than the endpoint allows — narrow it with a jurisdiction filter (do not blind-retry) |
| 429 | rate_limited · quota_exceeded | per-minute rate or monthly quota — see headers |
| 503 | unavailable | transient service problem — safe to retry shortly |
A rate-limit 429 carries Retry-After and
X-RateLimit-Limit / X-RateLimit-Remaining; a quota 429 carries
X-Quota-Limit / X-Quota-Used (resets at month end). Requests
refused with 429 are never counted against your usage.
coverage.last_ingested_date tells you
how current a register's numbers are.Support
Integration questions: support@pinakelai.com · commercial: hello@pinakelai.com