Endpoints
Every path is under https://patentref.io/api/v1/. A list endpoint answers q; a detail endpoint takes the key in the path (for example /patent/7861317/) and answers 404 {"detail": "Not found."} for an unknown key. On a detail route q and s are accepted and ignored (the path key selects the record; f and o apply), and a withdrawn patent's detail route answers 200 whatever o.exclude_withdrawn says (only the list route hides withdrawn patents). A "/" in a key is written ":" in the path: CPC group G01S7/4811 is /cpc_group/G01S7:4811/ and USPC subclass 100/1 is /uspc_subclass/100:1/, as the links inside records show; the slash form answers 404. Any field may be null where the bulk data has no value (for example patent_term_extension, and patent_detail_desc_length and the two average processing-day fields, which the build does not fill). Default fields in parentheses.
Granted patents and their records
| Endpoint | Routes | Response key | In data version 20261027.2 |
|---|---|---|---|
| patent | /api/v1/patent/ /api/v1/patent/{pk}/ | patents | served |
| patent/us_patent_citation | /api/v1/patent/us_patent_citation/ /api/v1/patent/us_patent_citation/{pk}/ | us_patent_citations | served |
| patent/us_application_citation | /api/v1/patent/us_application_citation/ /api/v1/patent/us_application_citation/{pk}/ | us_application_citations | served |
| patent/foreign_citation | /api/v1/patent/foreign_citation/ | foreign_citations | served |
| patent/other_reference | /api/v1/patent/other_reference/ | other_references | served |
| patent/rel_app_text | /api/v1/patent/rel_app_text/ | rel_app_texts | served |
| patent/attorney | /api/v1/patent/attorney/ /api/v1/patent/attorney/{pk}/ | attorneys | served |
Inventors, assignees and locations
| Endpoint | Routes | Response key | In data version 20261027.2 |
|---|---|---|---|
| inventor | /api/v1/inventor/ /api/v1/inventor/{pk}/ | inventors | served |
| assignee | /api/v1/assignee/ /api/v1/assignee/{pk}/ | assignees | served |
| location | /api/v1/location/ /api/v1/location/{pk}/ | locations | served |
Classifications
| Endpoint | Routes | Response key | In data version 20261027.2 |
|---|---|---|---|
| cpc_class | /api/v1/cpc_class/ /api/v1/cpc_class/{pk}/ | cpc_classes | answers 501 in this build |
| cpc_subclass | /api/v1/cpc_subclass/ /api/v1/cpc_subclass/{pk}/ | cpc_subclasses | answers 501 in this build |
| cpc_group | /api/v1/cpc_group/ /api/v1/cpc_group/{pk}/ | cpc_groups | served |
| uspc_mainclass | /api/v1/uspc_mainclass/ /api/v1/uspc_mainclass/{pk}/ | uspc_mainclasses | answers 501 in this build |
| uspc_subclass | /api/v1/uspc_subclass/ /api/v1/uspc_subclass/{pk}/ | uspc_subclasses | served |
| ipc | /api/v1/ipc/ | ipcr | served |
| wipo | /api/v1/wipo/ /api/v1/wipo/{pk}/ | wipo | answers 501 in this build |
| nber_category | /api/v1/nber_category/ /api/v1/nber_category/{pk}/ | nber_categories | served |
| nber_subcategory | /api/v1/nber_subcategory/ /api/v1/nber_subcategory/{pk}/ | nber_subcategories | served |
Long text of granted patents
| Endpoint | Routes | Response key | In data version 20261027.2 |
|---|---|---|---|
| g_claim | /api/v1/g_claim/ | g_claims | served |
| g_brf_sum_text | /api/v1/g_brf_sum_text/ | g_brf_sum_texts | served |
| g_detail_desc_text | /api/v1/g_detail_desc_text/ | g_detail_desc_texts | served |
| g_draw_desc_text | /api/v1/g_draw_desc_text/ | g_draw_desc_texts | served |
Pre-grant publications
| Endpoint | Routes | Response key | In data version 20261027.2 |
|---|---|---|---|
| publication | /api/v1/publication/ /api/v1/publication/{pk}/ | publications | answers 501 in this build |
| publication/rel_app_text | /api/v1/publication/rel_app_text/ | rel_app_texts | answers 501 in this build |
| pg_claim | /api/v1/pg_claim/ | pg_claims | answers 501 in this build |
| pg_brf_sum_text | /api/v1/pg_brf_sum_text/ | pg_brf_sum_texts | answers 501 in this build |
| pg_detail_desc_text | /api/v1/pg_detail_desc_text/ | pg_detail_desc_texts | answers 501 in this build |
| pg_draw_desc_text | /api/v1/pg_draw_desc_text/ | pg_draw_desc_texts | answers 501 in this build |
PatentRef additions
Expiry and maintenance-fee status, nearest patents and the record page: Lapse fields.
The claim tree of a patent, /api/v1/structure/claims/{patent_id}/: Claim trees.
A brief of a patent cut to a token budget, /api/v1/structure/brief/{patent_id}/, and the links to each claim and element on the record page: Patent briefs.
The claim counts of a patent, /api/v1/structure/counts/{patent_id}/: Claim counts.
The triage table (X-Api-Key, as every data path)
- POST /structure/triage/ (GET takes the same fields as parameters; the trailing slash is optional): {"patents": [...] or "<pasted text>"} or {"search": "<words>", "limit": <1 to 500, default 100>}, with "components" (optional) and "output" ("json", the default, or "csv"). The answer: {"error": false, "data_version", "text_data_version", "as_of", "structure_version", "generator", "input", "components": [{"key", "from", "asked", "patents"}], "positions", "complete", "time_limit_seconds", "status_available", "counts": {"patents", "found", "built", "not_built"}, "quotes", "export_columns", "patents": [...]}. It carries X-Data-Version and X-Text-Data-Version. A list of 500 numbers is too long for a GET request line: send it by POST.
- The patents: a list of up to 500 numbers (a JSON list, or text with the numbers separated by spaces, commas, semicolons or new lines; US, thousands separators such as 10,905,426, hyphens and kind codes such as B2 are read, and pieces that are not patent numbers are listed in input.unread), in the order given, each once; or a search: the claims that hold every word of search (at most 12 words and "quoted phrases"), read from the full-text index of the claims in the reverse of the order the long-text file loaded them (the bulk files year by year, oldest first, so recent grants tend to come first), each patent once, the first limit of them. input.more says more patents match; input.complete is false when the search stopped at its 5 s.
- A row: {"row", "patent_id", "title", "patent_date", "permalink", "found", "built", "note", "status", "claims": {"total", "independent"}, "cells", "components", "claim_parts"}. status holds status, basis, reinstatement_possible, reinstatement_basis, expiry_date_estimated, lapse_date, reinstated_date and as_of: the values GET /lapse/status/{patent_id}/ gives (null for a number the data in service does not have). note says why a row has no cells: not_in_data, no_claim_text (granted after the long-text file's last grant date), work_cap (over the claim tree's 1,000 claim rows or 1,000,000 characters), offset_check (a span that did not hold) or time_limit (below).
- Components: up to 12 in components, each read as a claim tree's term key (the noun phrase the term finder reads, lower case, the last word singular: "drive motors" is drive motor; words it does not read as a noun phrase stay as written); without any, the table picks up to 6: the terms the most of its patents introduce, each in at least two of them, weighed by ln(N / df) of the term's rarest word in titles and abstracts. components[].from is request or table, and patents counts the table's patents whose claims mention it.
- A cell, one per component in the table's order: {"key", "position", "match", "claim_keys", "independent_claims", "dependent_claims", "quote"}. position: every_independent (each independent claim mentions the component), some_independent (some independent claims do), only_dependent (no independent claim does, and a dependent claim does) or absent (no claim does). A claim mentions a component when one of its term mentions (an "a" or "the" phrase of the claim tree) has the component's key, or refers back to a mention that has it ("the device" matched to "an electronic device"). match is exact (the same key) or partial (one key ends with the other as whole words: drive motor and electric drive motor), and claim_keys names the claim terms' keys it matched. quote is the first mention, in the first independent claim that has one, else the first claim: {"claim", "claim_id", "term_id", "start", "end", "text"}.
- A row's components: the keys of the terms its own claims introduce, grouped as every_independent, some_independent and only_dependent. claim_parts: each independent claim as {"claim", "claim_id", "category", "parts": [{"part", "claim", "claim_id", "part_id", "start", "end", "text"}]}, part preamble, transition, element or wherein_clause in the order of the claim's text (an element can add label, its own label in the text, and opens_list); part_id is the part's id in the claim tree of GET /structure/claims/{patent_id}/.
- Quotes: text is the claim's text from start to end (Unicode code points, as in the claim tree), and the server checks every quote against the claim text before the answer leaves; quotes counts them.
- output=csv: the export, RFC 4180 (comma, CRLF, a field quoted when it holds a quote, a comma or a line break), UTF-8 without a byte order mark, the columns of export_columns: patent_id, title, status, status_basis (the basis codes joined by semicolons), reinstatement_possible, as_of, note, row_kind (patent, component, preamble, transition, element or wherein_clause), component, position, match, claim, claim_id, part_id, label, quote, start, end, mark. One row per patent, one per component of each row with claims, and one per claim part. The mark column is empty here: have and lacks are the reader's own notes, which the page https://patentref.io/triage/ keeps in the browser and writes into its own export (the same columns); the server never receives a mark.
- Limits and errors: one table at a time per key, and a few at once on the server; beyond that 429 with Retry-After and an X-Status-Reason that says so. A table stops reading claims after 20 s: the rows it did not reach say note time_limit and complete is false; send them again (trees a server process already read come back faster). One request toward the monthly allowance, whatever the number of patents. A request the table does not read is 400 ERR_Q, X-Status-Reason saying which field; with no long-text file in service the path answers 501 ERR_NOT_IMPLEMENTED, as a search does when that file has no full-text index of the claims. The table describes the text of the claims and nothing more.
The rules layer (X-Api-Key, as every data path; office is US, EP, CN, JP, KR, AU, or PCT for the international phase)
- Every answer: {"error": false, "tool", "query", "rules_version", "as_of", "count", "rows": [...], "note"}. A row: {"id", "office", "rule_kind", "act", "rule_text", "value", "effective_from", "effective_to", "superseded_by", "citation_url", "citation_quote", "extra_quotes", "written_by", "verified_on", "verified_by", "verification_method", "version", "notes"}. citation_url is the official page the row was read from (the office's own page, the statute or regulation, or the WIPO PCT Applicant's Guide national chapter); citation_quote is verbatim from it, under 300 characters. effective_from 1900-01-01 means the cited source states no start. Records of what the rules say, not legal advice.
- GET /rules/: the version, as_of, the offices with their row counts per rule_kind, the tools with their parameters, and the act vocabulary per rule_kind.
- GET /rules/rep_required/?office=&act=: representation rows (when an applicant must act through a registered agent). GET /rules/fee_schedule/?office=&as_of=YYYY-MM-DD: the fee rows in force on as_of (default today, UTC), amounts with their currency. GET /rules/route_windows/?office=: generic windows (months from a named event such as the priority date). GET /rules/pph_routes/?from_office=&to_office=: the PPH rows of to_office whose source names from_office as a partner. GET /rules/ai_use_statement_required/?office=: an empty answer means no cited rule, and nothing is inferred from it. GET /rules/grace_period/?office=: citations only. GET /rules/changelog/?since=x.y.z or YYYY-MM-DD: the releases after it.
- No rules path takes an application number, a patent number or a priority date (such a parameter answers 400 ERR_Q), and none computes a date for an application. An unknown parameter or a value out of its list answers 400 ERR_Q with the reason in X-Status-Reason.
Build facts, health, status, accounts (no key needed unless stated)
- GET /meta/: {"service", "base", "data_version", "build_id", "release", "as_of", "refreshed_at", "built_at", "snapshot_sha256", "index_set", "previous_version", "superseded_versions", "delta": [{"table", "current", "next", "added", "removed", "verdict"}], "delta_note", "fee", "weekly_grants", "deferred", "rules_version", "attribution", "licenses_url", "guide", "openapi", "generated_at"}. rules_version: the version of the rules dataset in service (null when it cannot be read). superseded_versions: builds that were ready and are not the one in service, in version order; a newer build waiting for its swap is never listed, and a build that served and was rolled back is. A permalink with ?v= one of them shows the superseded banner. delta: rows per table of this build against the one it replaced. The names come from the build step, which compared the build then in service with the new one: "current" is the build this one replaced and "next" is this build, the one in service now. verdict is the count rule on the PatentsView rows of the table (equal, +N.NN% within the growth limit, GREW>limit, SHRANK, new table, missing), followed after a semicolon by the rows the weekly grants added: "equal; grants +286,939" on patents means the PatentsView rows did not change and the weekly grants added 286,939. fee: the build's maintenance-fee delta, {"current", "next", "flags"}, where current and next (same naming) each carry {"lapsed", "max_lapse_date", "events", "file"}, or null. weekly_grants: the weekly grants the file carries beyond the PatentsView release, or null. deferred: the data sets the files in service lack, read from those files, one string each: the name, a colon, the routes that answer 501 without it and, for a classification lookup, where each patent carries the same facts ("wipo: /api/v1/wipo/ (each patent's WIPO fields come in its wipo group)"); empty when nothing is missing.
- GET /meta/health/: {"ok", "version", "data_version", "db", "time", "text_data_version", "patentref_version"}; 200 when the database answers, else 503; HEAD answers the same status with no body. version and patentref_version are the commits of the server code.
- GET /meta/status/ (HTML) and /meta/status.json: the public status figures, {"generated_at", "cache_seconds", "state", "service", "uptime", "external_check", "uptime_official", "requests_24h", "requests_30d", "refresh", "box", "alerts"}, cached 60 s. state is ["ok" or "degraded" or "down", a sentence]; uptime is the GitHub probe's record; external_check is UptimeRobot's check of /meta/health/ from its own network (state, state_since, last_check_at, interval_minutes, incidents, fresh); uptime_official is the uptime figure since 2026-10-06 03:47 UTC, the GitHub probe's down time until 2026-10-15 02:57:08 UTC and UptimeRobot's incidents after it (uptime_percent, down_minutes, switch_at, note); requests_24h and requests_30d count the answers on the data paths under /api/v1/ (requests, ok, client_errors, rejected for 403 and 429, server_errors, not_served, p50_ms, p95_ms, max_ms, error_rate, server_error_rate): not_served is the documented 501 of an endpoint whose data set is not in this release (the deferred list of /api/v1/meta/) and is not a server error, and the requests the service sends to check itself are left out of the figures and counted in own_requests; refresh.current and refresh.next are the build in service and the next build with their gates; box shows the free share of the system and data disks and of memory with an ok flag, the last backup and the watchdog checks. No server paths, internal alert ids or disk and memory sizes appear in it.
- GET /meta/signup/, GET and POST /meta/keys/ (bearer), GET /meta/usage/ (key or bearer): the keys page.
- https://patentref.io/LICENSES.md: every table, source and quote with its license and attribution line.