# Antecedint — full API and MCP reference > One plain-text document holding every public REST endpoint and every MCP tool. > Generated from the source registries, so it cannot disagree with the live server. ## Before you start Every request carries an API key as a bearer token: ``` Authorization: Bearer ak_live_... ``` Sign in at https://antecedint.com/account and a key is issued immediately. Google, Microsoft and an email code all reach the same account. A new account starts on the free tier at 100 requests a day; allowlisted design partners are upgraded to 100,000 automatically. A key is shown once, on the page that issues it. Only a salted PBKDF2 hash is stored, so it cannot be shown again; rotate from the same page if you lose it. The first 16 characters are a public handle you can quote in an email or grep your own logs for. Keys expire 90 days after issue unless a different term was agreed. The examples below use two variables: ```bash export BASE="https://mcp.antecedint.com/api/v1" export ANTECEDINT_API_KEY="ak_live_..." ``` ### Response envelopes Two shapes are in use, and which one you get depends on the route: - `/prior-art` answers `{ success, data }`, and its errors are `{ success: false, error }` with no `code` field. - `/patent-map` and `/claim-validation` answer the payload directly. Patent-map errors carry `{ error, code, timestamp }`; claim-validation errors carry `{ error }` alone. Branch on the HTTP status rather than on the presence of a field, and treat `code` as absent unless the table for that endpoint lists one. ### Errors shared by every endpoint | Status | Code | Meaning | |---|---|---| | `401` | `MISSING_API_KEY` | No Authorization header. | | `401` | `INVALID_AUTH_FORMAT` | The header is present but is not `Bearer `. | | `401` | `INVALID_KEY_FORMAT` | The header is well formed, but the key is not the shape described below. Checked before any lookup, so this says nothing about whether the key exists. | | `401` | `KEY_NOT_FOUND` | The key is correctly shaped but does not exist, is suspended, or was revoked. | | `401` | `API_KEY_EXPIRED` | The key is past its expiry date. | | `403` | `TEST_KEY_FORBIDDEN` | An `ak_test_` key was sent to a deployment that accepts live keys only. | | `403` | `IP_NOT_WHITELISTED` | The key has an IP allowlist and this request came from outside it. | | `403` | `INSUFFICIENT_PERMISSIONS` | The key is valid but lacks the permission this endpoint requires. The body names the missing one. | | `429` | `USAGE_LIMIT_EXCEEDED` | Over the daily or monthly quota. The body reports which limit fired and when it resets. | | `500` | `AUTH_SYSTEM_ERROR` | Key validation itself failed. Retry. | | `503` | | The instance is draining for a deploy. Retry. | These four are deliberately distinct rather than one generic 401, because together they locate a fault without guesswork: `INVALID_AUTH_FORMAT` means the header is wrong, `INVALID_KEY_FORMAT` means the header is right and the key is malformed, and `KEY_NOT_FOUND` means both are right and the key simply is not ours. Receiving the last one proves your base URL, header and scheme are all correct. ### Key format `ak_live_` followed by 32 alphanumeric characters, 40 in total. Test keys carry `ak_test_` and are refused by deployments configured for live keys. The format is validated before the key is looked up, which is why a truncated key returns `INVALID_KEY_FORMAT` rather than `KEY_NOT_FOUND`. Rate limits default to 1,000 requests a day and 30,000 a month, set per key at issue. # REST API ## Prior-art search Two-stage neural retrieval over 10.5M US applications. ### POST /prior-art Search 10.5M US patents for prior art against a claim. Cost: metered · MCP tool: `search_prior_art` Semantic prior-art search for a claim set. A fine-tuned dense encoder retrieves candidates from the Index-1 vector space, and a citation-graph channel contributes references an embedding alone would miss. Each result carries its title, abstract and claims. It does not carry a specification body: this used to promise background, summary and detailed description, which was true while the route downloaded each hit's SPEC document from USPTO and ran OCR and a model over it on the request path. That cost 18-55s per search and came off the live path on 2026-08-24. Context now comes from the corpus we already hold, and `description_text` is unpopulated there, so those sections are not available at any latency. `metadata.context_coverage` reports how many results actually carried each part, so a thin answer is distinguishable from a failed lookup. Either `claims` or `abstract` satisfies the request, but claim text retrieves better: the encoder was trained on `abstract [SEP] claim1`, so supplying both matches the document representation the index was built from. `metadata.total_cost` reports what the call actually spent on model inference, in USD. Latency runs 20-60s uncached and is dominated by specification parsing, not by retrieval; `rerank` adds to it. **Body** | Name | Type | Required | Description | |---|---|---|---| | `claims` | `string`, 10–20000 chars | no | Claim text; independent claim 1 works best. Required unless `abstract` is given. | | `abstract` | `string`, 0–10000 chars | no | Abstract or invention summary. Required unless `claims` is given. | | `count` | `integer`, 1–100 | no | Number of references to return. Defaults to `50`. | | `useReranking` | `boolean` | no | Run the ColBERT late-interaction reranker over the top candidates. Higher precision, slower. Defaults to `false`. | | `includeClaims` | `boolean` | no | Return full claim text and abstract sections for every reference. Off by default: the lookup is a BigQuery query with a job-submission floor of roughly a second whatever it scans, and a caller that ranks first and reads one or two pays that second for thirteen references it never opens. Fetch the ones you want with `POST /prior-art/claims`. While this is off, `title_source` on each result reads `namespace`, meaning the title came from the vector index rather than the authoritative lookup and may name an unrelated patent. Defaults to `false`. | | `hyde` | `boolean` | no | Rewrite the query into patent language before searching. A model drafts a hypothetical abstract and first claim for what you described, and that is searched instead. Worth setting when you have a description rather than claim text: the index holds formal claim language, and a plain-English query has to cross that gap unaided. Not worth setting when you already have a claim, which is the strongest query available. Adds about two seconds. `metadata.hyde` returns the generated text, because a rewritten query changes what was searched and the results cannot be judged without it. Defaults to `false`. | | `hybrid` | `boolean` | no | Also run BM25 over patent titles and abstracts and fuse the rankings. The encoder was trained on meaning, so it does not guarantee an exact phrase outranks a paraphrase; when you use a term of art, that term is the strongest signal available and dense search alone discards it. Measured: US-11389765-B2 was absent from the dense top 20 for a gyroid heat exchanger query and is rank 1 on BM25 over titles. Each result gains `matched_legs`, how many of the three retrievers found it, which is a sharper signal than a similarity score compressed into a 0.05 band. Defaults to `false`. | | `forceReprocess` | `boolean` | no | Bypass and overwrite every cache layer, re-parsing specifications that were already parsed. Costs real model spend on each call and is meant for reprocessing after a parser change, not for normal use. Defaults to `false`. | | `filedBefore` | `string` | no | Exclude references filed on or after this date. Prior art must predate the application it is cited against, so supply the priority date of the invention you are searching for; without it the results include later filings, which cannot be cited. `YYYY`, `YYYY-MM` or `YYYY-MM-DD`. Precision depends on the backend and `metadata.filter` reports what was applied: the turbopuffer index stores filing year only, so a mid-year bound admits later filings from the same year. It errs toward showing too much rather than too little. | | `filedAfter` | `string` | no | Exclude references filed on or before this date. For narrowing to a period, not for prior art. | | `cpc3` | `string[]` | no | Restrict to these CPC subclasses, e.g. `["H01M","F28D"]`. Useful for a second pass once a first search has shown which art areas matter. Note that CPC data covers roughly 65% of the index, and a reference carrying no CPC is excluded by this filter rather than ranked lower — so narrowing by subclass also drops everything unclassified. Prefer it for a follow-up pass, not a first search. | | `country` | `string` | no | Restrict to one country code, e.g. `US`. | **Responses** | Status | Meaning | |---|---| | `200` | `{ success: true, data: { specifications[], metadata } }`. `metadata` carries `total_cost` in USD, `request_id`, and `timestamp`. | | `400` | Neither `claims` nor `abstract` was supplied, both were empty, `count` fell outside 1-100, `forceReprocess` was not a boolean, or a date bound was not an ISO date. A malformed date is refused rather than dropped: a prior-art search that silently loses its cutoff returns references that postdate the invention while appearing to have honoured the request. The body is `{ success: false, error }` with no `code` field. | | `500` | Search failed. The body carries `request_id`; quote it when reporting. | **Example** ```bash curl -sS "$BASE/prior-art" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"claims":"1. A method of cooling a battery pack, comprising...","count":25}' ``` **Example response** (abridged; values are illustrative) ```json { "success": true, "data": { "specifications": ["one structured specification per reference, ranked; elided here"], "metadata": { "requested_count": 25, "returned_count": 25, "cached_count": 19, "newly_processed_count": 6, "cache_hit_rate": 0.76, "processing_time_seconds": 34.2, "textract_cost": 0.04, "bedrock_cost": 0.14, "total_cost": 0.18, "request_id": "req-1755162764108-k3f9a2b1c", "timestamp": "2026-08-14T09:12:44.108Z" } } } ``` ### POST /prior-art/claims Fetch claim text and abstracts for specific applications. Cost: metered · MCP tool: `get_patent_claims` Full text for references you already have. WHY THIS IS SEPARATE FROM THE SEARCH The claims lookup is a BigQuery query, and BigQuery charges roughly a second of job submission whatever it scans. Folding it into every search made the ranking wait on text the caller had not asked for yet: fifteen references fetched in full so one or two could be read. Splitting it lets a search answer in the time the search actually takes, and puts the second on whoever wants the text. Batched deliberately. One call for the three references worth reading costs the same second as one call for one, and the alternative is three round trips that each pay it. Endpoint docs live in the @endpoint block; see docs/api/doc-comments.md. **Body** | Name | Type | Required | Description | |---|---|---|---| | `appNumbers` | `string[]` | yes | US application numbers to fetch, digits only (`16115156`), 1 to 25 per call. These are the `application_number` values a search returns. | | `format` | `string` | no | `parsed` returns claims only as the structured `claims` array. `raw` also includes the unparsed claim blob as a section, which is the same text a second time and roughly doubles the response. Use `raw` only when the parse itself is in question. Defaults to `parsed`. | | `independentOnly` | `boolean` | no | Return only independent claims. Triage usually needs claim 1, and a record can carry two dozen. The number dropped is reported as `metadata.claims_withheld` so a short answer cannot be mistaken for a short patent. Defaults to `false`. | **Responses** | Status | Meaning | |---|---| | `200` | `{ success: true, data: { specifications[], metadata } }`. One entry per application number that was found, in the order asked for. An application with no row in the lookup table is reported in `metadata.not_found` rather than returned empty, because "this patent has no claims on file" and "we could not fetch them" are opposite conclusions. | | `400` | `appNumbers` was missing, empty, not an array, held something that is not a digit string, or held more than 25 entries. | | `500` | The lookup failed. The body carries `request_id`. | **Example** ```bash curl -sS "$BASE/prior-art/claims" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"appNumbers":["16115156","17003222"]}' ``` **Example response** (abridged; values are illustrative) ```json { "success": true, "data": { "specifications": ["one structured specification per application; elided here"], "metadata": { "requested": 2, "returned": 2, "not_found": [], "lookup_ms": 1120, "request_id": "req-1755162764108-k3f9a2b1c" } } } ``` ## Patent space Positions, neighbours, citations and gaps in one shared coordinate frame. ### GET /patent-map/patent/:appNum Patent position and its nearest semantic neighbours. Cost: metered · Cached `private, max-age=86400` · MCP tool: `get_patent_neighborhood` One call frames a patent's neighbourhood, which is the payload behind the display-mode embed: `{ self, neighbors[], count }`. `self` is null when the application has no vector in the index, which happens for pre-corpus, design, and plant applications. The application can still be absent entirely, and that answers 404 rather than a null `self`. Neighbour order is cosine similarity in the embedding space, not 3D distance. Semantically closest comes first; the coordinates are for laying them out, not for re-deriving the ranking. Distance in the projection and rank in the list disagree often enough that sorting by one and labelling it the other will mislead. **Path parameters** | Name | Type | Required | Description | |---|---|---|---| | `appNum` | `string`, `^\d{7,12}$` | yes | US application number, digits only. | **Query parameters** | Name | Type | Required | Description | |---|---|---|---| | `topK` | `integer`, 1–100 | no | Number of neighbours to return. Defaults to `20`. | **Responses** | Status | Meaning | |---|---| | `200` | `{ appNum, self, neighbors[], count, timestamp }`. Each neighbour is a point plus `score`, the cosine similarity. | | `400` `VALIDATION_ERROR` | appNum is not 7-12 digits, or topK is outside 1-100. | | `404` `NOT_INDEXED` | No vector is indexed for this application. | | `502` `UPSTREAM_ERROR` | The vector index could not be reached. | **Example** ```bash curl -sS "$BASE/patent-map/patent/16123456?topK=5" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` **Example response** (abridged; values are illustrative) ```json { "appNum": "16123456", "self": { "appNum": "16123456", "x": 4.771, "y": -1.02, "z": 2.503, "section": "H", "artUnit": "1727", "filingDate": "2019-05-14" }, "neighbors": [ { "appNum": "16412088", "x": 4.802, "y": -1.114, "z": 2.397, "section": "H", "artUnit": "1725", "filingDate": "2019-08-02", "score": 0.9012 } ], "count": 5, "timestamp": "2026-08-14T09:12:44.108Z" } ``` ### GET /patent-map/patent/:appNum/citations Cited prior art resolved to patent-space positions. Cost: metered · Cached `private, max-age=86400` · MCP tool: `get_patent_citations` The application's cited prior art, resolved to map positions. Each point carries `examiner: true` when the reference came from the examiner rather than from the applicant's IDS, which is the distinction that matters: examiner citations are what was actually applied against the application. Examiner-cited references lead the list and are never dropped. Applicant citations fill the remainder up to 150 references total, so a 400-reference IDS cannot balloon the response. Pass `year`, the citing application's filing year, when you know it. It narrows the underlying lookup and makes the call measurably cheaper. References with no vector in the index are counted in `notIndexed` and omitted from `points`. Positions on this API are always exact index coordinates; nothing is placed approximately to fill a gap. **Path parameters** | Name | Type | Required | Description | |---|---|---|---| | `appNum` | `string`, `^\d{7,12}$` | yes | US application number, digits only. | **Query parameters** | Name | Type | Required | Description | |---|---|---|---| | `year` | `integer`, 1970–2035 | no | Filing year of the citing application. Optional, and makes the lookup faster when supplied. | **Responses** | Status | Meaning | |---|---| | `200` | `{ appNum, points[], count, citedTotal, notIndexed, timestamp }`. Each point carries `examiner` and `title` alongside the coordinates. | | `400` `VALIDATION_ERROR` | appNum is not 7-12 digits, or year is implausible. | | `502` `UPSTREAM_ERROR` | The citation service or vector index could not be reached. | **Example** ```bash curl -sS "$BASE/patent-map/patent/16123456/citations?year=2019" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` ### POST /patent-map/locate Locate free text or an application in patent space. Cost: metered · MCP tool: `locate_in_patent_space` Semantic search into the space. Free text is embedded and matched against the index; given an `appNum` instead, the application's own abstract and first claim are joined as `abstract [SEP] claim1` and used as the query, which reproduces the document representation Index-1 was built from and retrieves better than anything typed by hand. Alongside the nearest patents it returns `landing`, a rank-weighted centroid of the top 8 hits. That is where the query sits in the space and the anchor a camera should fly to. It is not a patent and has no application number. This is the one endpoint on the patent-map router that spends an embedding-hub encode per call, so it is the one that does not answer from cache. **Body** | Name | Type | Required | Description | |---|---|---|---| | `query` | `string`, 8–4000 chars | no | Free-text technical description. Supply either this or `appNum`; `query` wins when both are present. | | `appNum` | `string`, `^\d{7,12}$` | no | Locate an application by its stored vector. The application itself is excluded from `neighbors`. | | `topK` | `integer`, 1–100 | no | Number of nearest patents to return. Defaults to `20`. | **Responses** | Status | Meaning | |---|---| | `200` | `{ landing: {x,y,z}, self?, neighbors[], count, located_by, timestamp }`. `located_by` is `stored_vector` (the index holds the application: `landing` is its stored point, returned as `self`, and neighbour scores are against the document vector), `re_encoded_text` (it does not: the text was encoded as a query, scores top out near 0.87 and `landing` is a centroid; a `note` says so), or `query_text`. | | `400` `VALIDATION_ERROR` | Neither query nor appNum was supplied, or one of them failed its length or format rule. | | `404` `NOT_FOUND` | No text could be found for the application, or no indexed match carried map coordinates. | | `502` `UPSTREAM_ERROR` | The embedding hub or vector index could not be reached. | | `503` `MAP_COORDINATES_UNAVAILABLE` | The vector index this deployment is configured against does not store UMAP coordinates, so nothing can be placed in patent space. Prior-art search is unaffected. | **Example** ```bash curl -sS "$BASE/patent-map/locate" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"query":"phase-change material for battery thermal management","topK":10}' ``` **Example response** (abridged; values are illustrative) ```json { "landing": { "x": 4.812, "y": -1.093, "z": 2.446 }, "neighbors": [ { "appNum": "16412088", "x": 4.771, "y": -1.02, "z": 2.503, "section": "H", "artUnit": "1727", "filingDate": "2019-05-14", "score": 0.8814 } ], "count": 10, "timestamp": "2026-08-14T09:12:44.108Z" } ``` ### GET /patent-map/whitespace Ranked gaps in patent space. Free · Cached `private, max-age=1800` · MCP tool: `find_whitespace` The analysed whitespace atlas: gaps in patent space, ranked by opportunity, descending. Built for the inventor-facing question of where the open ground is. Each void carries an id, a position in the same coordinate frame as every other endpoint on this router, a radius, an opportunity score from 0 to 1, a CPC section, the bordering CPC codes with their fractions and titles, a temporal profile giving the median filing year of the surrounding art and a `hot` flag, a generated summary of what is missing there, the nearest bordering patents as exemplars, and the assignees active nearby. The atlas is a precomputed artifact, rebuilt on its own schedule rather than on request, and cached for 30 minutes once loaded. Two calls a minute apart return the same voids. **Query parameters** | Name | Type | Required | Description | |---|---|---|---| | `limit` | `integer`, 1–500 | no | Maximum voids to return. Defaults to `100`. | | `section` | `string`, `^[A-HY]$` | no | Restrict to one CPC section letter. | | `minOpportunity` | `number`, 0–1 | no | Drop voids scoring below this. Defaults to `0`. | **Responses** | Status | Meaning | |---|---| | `200` | `{ voids[], count, total, timestamp }`. `total` counts everything matching the filters; `count` is how many `limit` allowed through. | | `400` `VALIDATION_ERROR` | limit, section, or minOpportunity is out of range. | | `404` `NOT_FOUND` | The whitespace atlas has not been built. | | `502` `UPSTREAM_ERROR` | The atlas could not be read from storage. | **Example** ```bash curl -sS "$BASE/patent-map/whitespace?section=G&minOpportunity=0.6&limit=20" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` ### GET /patent-map/points Sampled background point cloud. Cost: metered · Cached `private, max-age=1800` A sampled background cloud for context rendering, up to 50,000 points per request. The sample pages the index in stored order. It is stable across calls, and it is not random: the first 10,000 points are the same 10,000 points every time, and they are not a representative sample of the corpus. Filter server-side with `section` and `artUnit` rather than over-fetching and filtering client-side, because the scan stops at a fixed budget regardless of how many points survive your filter. That budget is roughly 150,000 scanned vectors. A filter narrow enough to exhaust it before collecting `limit` points returns what it found with `truncated: true`. Treat that flag as a signal to narrow differently rather than to retry, since the scan is deterministic and a retry returns the same partial sample. **Query parameters** | Name | Type | Required | Description | |---|---|---|---| | `limit` | `integer`, 100–50000 | no | Points to return. Defaults to `10000`. | | `section` | `string`, `^[A-HY]$` | no | Restrict to one CPC section letter. | | `artUnit` | `string`, `^\d{1,4}$` | no | Restrict to an art-unit prefix. | **Responses** | Status | Meaning | |---|---| | `200` | `{ points[], count, truncated, timestamp }` | | `400` `VALIDATION_ERROR` | limit, section, or artUnit is out of range. | | `502` `UPSTREAM_ERROR` | The vector index could not be reached. | **Example** ```bash curl -sS "$BASE/patent-map/points?limit=5000§ion=H" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` ## §112 claim validation Antecedent basis and claim-term support, for drafts and for filed applications. ### POST /claim-validation/antecedent-basis Check a draft claim set for antecedent-basis defects. Free · MCP tool: `check_antecedent_basis` Check a claim set for §112(b) antecedent-basis defects. Finds definite recitations ("the widget", "said widget") with no earlier indefinite recitation in the same claim or in a claim it depends from, plus double inclusions, plural and singular mismatches, and improper dependencies. Implicit antecedent basis under MPEP 2173.05(e) is suppressed, so "the outer surface of the housing" is not flagged when the housing was properly introduced. Pass `includeSuppressed` to see what the suppression rules removed and which rule removed each one, which is how you audit the checker rather than trust it. Findings are graded. `high` means no plausible antecedent exists anywhere in the set; `low` means one probably exists under different wording and a human should look. Deterministic, no LLM, no network, and sub-millisecond per claim, so a draft editor can call it on every keystroke. **Body** | Name | Type | Required | Description | |---|---|---|---| | `claims` | `object[]`, max 300 | yes | The claim set, in order. Each entry is `{ number?, text }`; a bare string is accepted in place of an object, and a missing `number` is filled from the array position. | | `claims[].text` | `string`, 10+ chars | yes | Claim text without the leading number. | | `claims[].number` | `integer`, 1 and up | no | Claim number. | | `minSeverity` | `string`, one of `high`, `medium`, `low` | no | Lowest severity to report. Defaults to `low`. | | `includeSuppressed` | `boolean` | no | Also return the candidates the implicit-basis rules suppressed, each with the rule that killed it. Defaults to `false`. | **Responses** | Status | Meaning | |---|---| | `200` | `{ available: true, source: "request", issues[], claims[], dependencies[], stats }`. `issues[]` is the flat list of every defect found; each entry of `claims[]` also carries its own `issues[]`. `stats` holds `issueCount`, `bySeverity` and `byType`. An empty `issues[]` means the claim set is clean, and is the only thing that means that. | | `400` | No claim in `claims[]` carried usable text, or `minSeverity` was not one of high, medium, low. The body is `{ error }` with no `code` field. An unrecognised `minSeverity` is refused rather than defaulted, because silently returning the unfiltered list looks like a filtered one. | | `500` | The check itself failed. | **Example** ```bash curl -sS "$BASE/claim-validation/antecedent-basis" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"claims":[{"number":1,"text":"A device comprising a housing."}, {"number":2,"text":"The device of claim 1, wherein the lid is hinged."}]}' ``` **Example response** (abridged; values are illustrative) ```json { "available": true, "source": "request", "claims": [ { "number": 2, "isIndependent": false, "dependsOn": [1], "introducedTerms": [], "issues": [ { "claimNumber": 2, "term": "lid", "display": "the lid", "type": "no-antecedent-basis", "severity": "high", "start": 34, "end": 41, "snippet": "The device of claim 1, wherein the lid is hinged." } ] } ], "stats": { "claimCount": 2, "independentCount": 1, "issueCount": 1, "claimsWithIssues": 1, "bySeverity": { "high": 1, "medium": 0, "low": 0 }, "byType": { "no-antecedent-basis": 1 } } } ``` ### GET /claim-validation/antecedent-basis/:appNum Check the filed claims of an application. Cost: metered · Cached `private, max-age=86400` · MCP tool: `check_antecedent_basis` The same check as POST /antecedent-basis, run over the claims already on file for an application rather than over claims you supply. An application whose claims are not in the warehouse answers 200 with `{ available: false, reason: "no-claims" }`, not 404. The distinction is deliberate: the application exists and the request was well formed, we just have nothing to check. Branch on `available` before reading `issues`. **Path parameters** | Name | Type | Required | Description | |---|---|---|---| | `appNum` | `string`, `^\d{7,8}$` | yes | US application number, digits only. | **Query parameters** | Name | Type | Required | Description | |---|---|---|---| | `minSeverity` | `string`, one of `high`, `medium`, `low` | no | Lowest severity to report. Defaults to `low`. | | `includeSuppressed` | `boolean` | no | Send `1` to include suppressed candidates with the rule that removed each. | **Responses** | Status | Meaning | |---|---| | `200` | `{ appNum, available: true, source: "warehouse", issues[], claims[], dependencies[], stats }`, shaped exactly as the POST above, or `{ appNum, available: false, reason: "no-claims" }` when nothing is on file. | | `400` | appNum is not a 7-8 digit US application number, or `minSeverity` was not one of high, medium, low. | | `502` | The claim pull from the warehouse failed. | **Example** ```bash curl -sS "$BASE/claim-validation/antecedent-basis/16123456?minSeverity=high" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` ### POST /claim-validation/support Map claim terms to their specification support. Cost: metered · MCP tool: `map_claim_support` Map every claim term to the specification passages that give it meaning, with exact locations: section, paragraph number, and character offsets. Each term is graded by how the spec supports it, not merely whether the word appears. A term can be formally defined, defined by the claim itself, scoped by enumeration, mapped to an embodiment, characterized functionally, merely described, or UNDESCRIBED. That last grade is the §112(a) written-description signal worth acting on. `claims[]` is always required on this endpoint. Use GET /support/:appNum when you want the filed claims analyzed instead of your own. Supply `sections` and the analysis runs against your own specification text and touches no network, which is what makes this usable on an unfiled draft. Supply `appNum` without `sections` and the filed specification is pulled for you. Supplying `appNum` with `includeParents` walks the continuity chain and checks each parent specification too, so a term supported here but in no ancestor is surfaced as new matter that cannot claim the parent's filing date. The walk needs `appNum`; it cannot run on text alone. **Body** | Name | Type | Required | Description | |---|---|---|---| | `claims` | `object[]`, max 300 | yes | Claim set to analyze, each `{ number?, text }`. Always required here; GET /support/:appNum uses the filed claims instead. | | `sections` | `object[]`, max 60 | no | Specification sections to ground against, each `{ type?, name?, text }`. Either this or `appNum` is required. | | `appNum` | `string`, `^\d{7,8}$` | no | US application number. Required for the continuity walk, and used to pull the spec when `sections` is omitted. | | `includeParents` | `boolean` | no | Walk the continuity chain and check each parent specification. Ignored without `appNum`. Defaults to `false`. | | `maxAncestors` | `integer`, 0–12 | no | Cap on ancestors to check. Defaults to `6`. | | `ocr` | `boolean` | no | Allow OCR for image-only filings. Most provisionals are image-only, so the walk usually needs this to read them. Defaults to `false`. | **Responses** | Status | Meaning | |---|---| | `200` | Per-term support grades with locations, plus the ancestor results when the continuity walk ran. | | `400` | `claims[]` was empty, or neither `sections[]` nor `appNum` was supplied. | | `500` | The analysis failed. | **Example** ```bash curl -sS "$BASE/claim-validation/support" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"appNum":"16123456","includeParents":true,"ocr":true, "claims":[{"number":1,"text":"A battery pack comprising a phase-change layer."}]}' ``` ### GET /claim-validation/support/:appNum Support map for a filed application and its parents. Cost: metered · Cached `private, max-age=86400` · MCP tool: `map_claim_support` The support map for an application already on file, using its filed claims and its filed specification. No claim text is supplied by the caller. The continuity walk runs by default here, which is the opposite of POST /support. Send `parents=0` to analyze the application's own specification alone. The walk is what makes this call slow and what makes it worth making: it is where new matter shows up. OCR is opt-in with `ocr=1` and is usually needed for the walk to be meaningful, because most provisionals are image-only scans with no extractable text layer. Without it an unreadable ancestor contributes nothing, and a term it actually supports can be reported as new matter. The flag is also killable server-side: when `SPEC_OCR_DISABLED` is set, `ocr=1` is ignored rather than refused. **Path parameters** | Name | Type | Required | Description | |---|---|---|---| | `appNum` | `string`, `^\d{7,8}$` | yes | US application number, digits only. | **Query parameters** | Name | Type | Required | Description | |---|---|---|---| | `parents` | `boolean` | no | Send `0` to skip the continuity walk. Defaults to `1`. | | `maxAncestors` | `integer`, 0–12 | no | Cap on ancestors to check. Defaults to `6`. | | `ocr` | `boolean` | no | Send `1` to allow OCR on image-only filings. | **Responses** | Status | Meaning | |---|---| | `200` | Per-term support grades with locations, plus per-ancestor results and any new-matter findings when the continuity walk ran. | | `400` | appNum is not a 7-8 digit US application number. | | `502` | The specification pull or the analysis failed. | **Example** ```bash curl -sS "$BASE/claim-validation/support/16123456?ocr=1&maxAncestors=4" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` ## Warm-up Start the inference hub before a search needs it, so the cold start is paid off the critical path. ### POST /warmup Start the inference hub ahead of a search. Free · Cached `no-store` · MCP tool: `warm_up` Start the inference hub before you need it. The hub scales to zero, so the first search after an idle period pays ~71s of model loading. Call this a minute or two ahead and that cost is paid off the critical path; the search that follows runs at its warm latency of a few seconds. The call returns immediately whether or not the hub is ready — it never blocks for the cold start. Poll `GET /warmup` to watch it become ready, or simply proceed and accept whatever state the hub is in. **Query parameters** | Name | Type | Required | Description | |---|---|---|---| | `rerank` | `boolean` | no | Also load the reranker, which `search_prior_art` needs when `useReranking` is set. Adds ~25s to the warm-up and is unnecessary for retrieval alone. Defaults to `false`. | | `wait` | `integer` | no | Seconds to wait for the models to finish loading before answering, up to 120. The default of 0 returns straight away and leaves the caller to poll. Set this and the call blocks until `ready` is true or the wait runs out, which is one round trip instead of a polling loop. Defaults to `0`. | **Responses** | Status | Meaning | |---|---| | `200` | `{ ready, warming, models, estimatedReadySeconds, warmWindowSeconds }`. `ready` true means a warm instance exists now; it is not a reservation. | | `502` `UPSTREAM_ERROR` | The inference hub could not be reached at all. | **Example** ```bash curl -sS -X POST "$BASE/warmup?rerank=true" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` **Example response** (abridged; values are illustrative) ```json { "ready": false, "warming": true, "models": { "encoder": false, "reranker": false }, "estimatedReadySeconds": 100, "warmWindowSeconds": 900, "note": "Cloud Run routes each request independently, so a warm instance makes a fast search likely rather than guaranteed." } ``` ### GET /warmup Whether the inference hub is warm. Free · Cached `no-store` Whether the inference hub is warm right now. Read-only: it reports state and starts nothing. Use it to poll after `POST /warmup`, or to decide whether a search is about to be fast before committing to it. **Responses** | Status | Meaning | |---|---| | `200` | `{ ready, models, warmWindowSeconds }`. `ready` reflects the encoder only; check `models.reranker` if the next call sets `useReranking`. | | `502` `UPSTREAM_ERROR` | The inference hub could not be reached at all. | **Example** ```bash curl -sS "$BASE/warmup" -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` **Example response** (abridged; values are illustrative) ```json { "ready": true, "models": { "encoder": true, "reranker": false }, "warmWindowSeconds": 900 } ``` ## Prosecution intelligence CPC scheme and empirical routing, art-unit behaviour, and examiner statistics. ### GET /patent-intel/cpc/:code CPC symbol with its art-unit distribution and children. Cost: metered · Cached `private, max-age=86400` · MCP tool: `lookup_cpc_code` One CPC symbol, resolved three ways at once: what the scheme says it means, which art units actually examine it, and what sits under it. The art-unit distribution is empirical rather than declared — it is where applications carrying this symbol were really routed, with the modal unit, the per-unit probabilities and the Shannon entropy of the spread. A low entropy means the symbol predicts the art unit; a high one means it does not, which is the more useful answer when deciding whether to argue classification. `children` is capped by `childLimit`, because a subclass can carry thousands of subgroups and a caller asking about `C11D` wants to know that it has children, not to receive all of them. `child_count` always reports the true total. The art-unit `distribution` is capped the same way by `distributionLimit`: it is sorted by count, `unique_art_units` is the true total, and a symbol examined by 222 units does not need all 222 listed to show that one of them takes 37%. **Path parameters** | Name | Type | Required | Description | |---|---|---|---| | `code` | `string`, `^[A-HY][0-9A-Z/]{0,18}$` | yes | CPC symbol, e.g. C11D or C11D7/261. | **Query parameters** | Name | Type | Required | Description | |---|---|---|---| | `childLimit` | `integer`, 0–500 | no | How many child symbols to return. Defaults to `50`. | | `distributionLimit` | `integer`, 0–500 | no | How many art units to list in `art_units.distribution`, largest first. Defaults to `20`. | **Responses** | Status | Meaning | |---|---| | `200` | `{ code, scheme, art_units, children[], child_count }`. `scheme` is null for a symbol with routing data but no scheme entry; `art_units` is null for a symbol nothing has been routed under. | | `400` `VALIDATION_ERROR` | code is not a CPC symbol, or a limit is outside 0-500. | | `404` `NOT_FOUND` | Neither the scheme nor the routing data knows this symbol. | | `503` `CORPUS_UNAVAILABLE` | The corpus database is not configured or not reachable. | **Example** ```bash curl -sS "$BASE/patent-intel/cpc/C11D?childLimit=5" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` **Example response** (abridged; values are illustrative) ```json { "code": "C11D", "scheme": { "code": "C11D", "title": "DETERGENT COMPOSITIONS", "level": "subclass", "parent": "C11" }, "art_units": { "level": "subclass", "total_count": 14476, "unique_art_units": 222, "top_art_unit": "1761", "top_art_unit_probability": 0.3689, "entropy": 4.3798, "distribution": [ { "au": "1761", "count": 5227, "probability": 0.36893 } ] }, "children": [ { "code": "C11D1/00", "title": "Detergent compositions", "level": "group" } ], "child_count": 132 } ``` ### GET /patent-intel/classification/:number CPC classification for a publication or application number. Cost: metered · Cached `private, max-age=86400` · MCP tool: `get_patent_classification` The CPC assignment on one published application or granted patent, as the USPTO Master Classification File records it. The number is tried as a publication number first and an application number second, so a caller holding either can ask without saying which it has. The main symbol is resolved against the scheme, so the answer carries a title and not only a code. `inventive_cpcs` are the symbols covering what is claimed; `additional_cpcs` cover subject matter disclosed but not claimed. Callers building a search from this usually want the inventive set alone. **Path parameters** | Name | Type | Required | Description | |---|---|---|---| | `number` | `string`, `^\d{7,12}$` | yes | Publication or US application number, digits only. | **Responses** | Status | Meaning | |---|---| | `200` | `{ publication_number, application_number, main_cpc, main_cpc_title, inventive_cpcs[], additional_cpcs[], ... }`. | | `400` `VALIDATION_ERROR` | number is not 7-12 digits. | | `404` `NOT_FOUND` | No classification record for this number. | | `503` `CORPUS_UNAVAILABLE` | The corpus database is not configured or not reachable. | **Example** ```bash curl -sS "$BASE/patent-intel/classification/20010000001" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` **Example response** (abridged; values are illustrative) ```json { "publication_number": "20010000001", "application_number": "09725796", "kind_code": "A1", "main_cpc": "C11D7/261", "main_cpc_title": "Organic compounds containing oxygen", "inventive_cpcs": ["C11D7/261", "C11D7/24"], "additional_cpcs": ["C11D7/267", "C11D7/28"] } ``` ### GET /patent-intel/art-unit/:artUnit Art-unit profile, statistics and examiner roster. Cost: metered · Cached `private, max-age=86400` · MCP tool: `get_art_unit_profile` One art unit: what it examines and how it behaves. Assembled from the four collections that describe a unit — the statistics, the descriptive record, the generated enrichment and the examiner roster — so a caller makes one request rather than four. `statistics` carries allowance and rejection behaviour, timing, interview receptiveness, RCE tolerance and estimated cost, averaged across the unit's examiners. Read `statistics.data_quality` before relying on any of it: it reports how many applications and office actions the averages rest on, and the volume, recency and completeness scores behind them. Units with thin data carry the same field names and much weaker numbers. The roster's application numbers are not returned. One unit carries tens of thousands of them, which is most of a 209 KB document and no use in a profile. The model-written `description` is not returned whole either: on 1725 it was 9,887 characters, 56% of the response, and it filed "bibs for hairdressers' rooms" under a materials unit. `generated_context` says the same in a paragraph, so `description` is served as its opening and its length. `examiner_count` is the length of the roster served. Where the stored statistics counted a different roster (1725 lists 28 examiners against a stored 27), the stored figure appears as `examiner_count_in_statistics` so the disagreement is visible rather than resolved silently. **Path parameters** | Name | Type | Required | Description | |---|---|---|---| | `artUnit` | `string`, `^\d{3,4}$` | yes | USPTO art unit, e.g. 2872. | **Responses** | Status | Meaning | |---|---| | `200` | `{ art_unit, technology_center, title, generated_context, description, keywords[], cpc_codes, examiners[], examiner_count, statistics }`. `cpc_codes` summarises rather than lists: a total, counts by CPC section, and the 25 commonest subclasses. The raw list runs to 24,135 symbols for a single unit and two unrelated units share 57% of it, so it described the corpus more than the unit and made this response 383 KB. `statistics` is null for a unit that has a descriptive record but no computed statistics; inside it, `claims_analysis.independentClaimsStats.avgIndependentClaims` is null with a note where the stored value tracked the total claim count. | | `400` `VALIDATION_ERROR` | artUnit is not 3-4 digits. | | `404` `NOT_FOUND` | No collection holds this art unit. | | `503` `CORPUS_UNAVAILABLE` | The corpus database is not configured or not reachable. | **Example** ```bash curl -sS "$BASE/patent-intel/art-unit/2872" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` **Example response** (abridged; values are illustrative) ```json { "art_unit": "2872", "technology_center": "2800", "title": "Optics: Measuring and Testing", "examiner_count": 63, "cpc_codes": ["G01B", "G02B"], "examiners": ["GREY CHRISTOPHER P", "YOUNG HUGH P"], "statistics": { "portfolio": { "total_ctnf_issued_avg": 902.86 }, "interview_metrics": { "interview_success_rate": 65.89 }, "data_quality": { "total_applications_analyzed": 53392, "office_actions_analyzed": 132188, "completeness_score": 5.54 } } } ``` ### GET /patent-intel/examiners Search examiners by name or art unit. Cost: metered · Cached `private, max-age=86400` · MCP tool: `find_examiner` Find examiners by surname or art unit. Matching is anchored: an exact surname, or a prefix of the full name in the USPTO's surname-first form. An unanchored substring search would be a regex over 10,308 documents averaging a megabyte each, which is a collection scan reading 11 GB to answer a lookup. Callers wanting a specific examiner should pass the surname and filter the result. **Query parameters** | Name | Type | Required | Description | |---|---|---|---| | `name` | `string`, 2–80 chars | no | Surname, or a prefix of the surname-first full name. | | `artUnit` | `string`, `^\d{3,4}$` | no | Restrict to one art unit. | | `limit` | `integer`, 1–100 | no | Maximum matches to return. Defaults to `20`. | **Responses** | Status | Meaning | |---|---| | `200` | `{ count, examiners[] }`, each `{ examiner_id, full_name, art_unit, technology_center }`. Empty when neither name nor artUnit is given. | | `400` `VALIDATION_ERROR` | A parameter failed validation. | | `503` `CORPUS_UNAVAILABLE` | The corpus database is not configured or not reachable. | **Example** ```bash curl -sS "$BASE/patent-intel/examiners?name=SMITH&artUnit=1609" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` **Example response** (abridged; values are illustrative) ```json { "count": 1, "examiners": [ { "examiner_id": "68a73eaad7496de7109778b7", "full_name": "SMITH ADAM M", "art_unit": "1609", "technology_center": "Technology Center 1600" } ] } ``` ### GET /patent-intel/examiner/:examiner Examiner statistics and behavioural profile. Cost: metered · Cached `private, max-age=86400` · MCP tool: `get_examiner_profile` One examiner's statistics, by examiner id or by USPTO surname-first name. Both forms are accepted because a caller reading an office action has the name and a caller following an art-unit roster has the id. The response carries rejection analytics, portfolio counts, interview and RCE behaviour, timing, cost analysis and percentile rankings against the unit and the corps, plus `claim_interpretation` where a model-written read of how this examiner construes claims exists — it does for 11 examiners, and is null for the rest rather than absent. Six sections are withheld by default and named in `sections_available`. Two are cold: `behavioral_patterns` and `rejection_analysis` hold per-application detail and are together 10.6 GB of the 11.1 GB collection, the largest single document being 14.2 MB. Four are hot but wide: `applications_with_interviews` and `applications_with_rcex` are per-application lists (hundreds of rows each), `by_sequence_length` and `document_code_breakdown` are histograms with hundreds of keys. Together they were 83% of a default response. Each is replaced by its count and the `include` name that brings it back; `document_code_breakdown` keeps its ten largest codes. `data_quality` reports what the numbers rest on, and says what was withheld or relabelled. `populations` names the five denominators that appear in one response (all applications, analysed, with documents, with office actions, with a filing date). `volume.tier` replaces an upstream score that was 5 for every examiner. `withheld[]` lists fields removed because their values were wrong and not recoverable, with what was observed; `notes[]` lists sections served as null because they were never computed (`percentile_rankings`, `claims_analysis` on every examiner sampled), and pairs of fields that disagree. **Path parameters** | Name | Type | Required | Description | |---|---|---|---| | `examiner` | `string`, 2–120 chars | yes | Examiner id, or USPTO surname-first full name. | **Query parameters** | Name | Type | Required | Description | |---|---|---|---| | `include` | `string` | no | Comma-separated sections to add: `behavioral_patterns`, `rejection_analysis` (cold; megabytes), `applications_with_interviews`, `applications_with_rcex`, `by_sequence_length`, `document_code_breakdown` (hot; thousands of tokens). Unknown names are ignored. | **Responses** | Status | Meaning | |---|---| | `200` | `{ examiner_id, full_name, art_unit, technology_center, portfolio, rejection_analytics, interview_metrics, rcex_metrics, timeline_metrics, strategic_insights, data_quality, claim_interpretation, sections_available[], sections_included[], ... }`. `percentile_rankings` and `claims_analysis` are null where never computed. | | `400` `VALIDATION_ERROR` | examiner is shorter than 2 characters or longer than 120. | | `404` `NOT_FOUND` | No examiner with this id or name. | | `503` `CORPUS_UNAVAILABLE` | The corpus database is not configured or not reachable. | **Example** ```bash curl -sS "$BASE/patent-intel/examiner/SMITH%20ADAM%20M" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` **Example response** (abridged; values are illustrative) ```json { "examiner_id": "68a883c3d8cb3a33568ff86d", "full_name": "HASAN MOHAMMED A", "art_unit": "2872", "technology_center": "2800", "portfolio": { "total_office_actions_issued": 6280 }, "percentile_rankings": null, "data_quality": { "total_applications_analyzed": 2711, "office_actions_analyzed": 6280, "populations": { "total_applications": 2711, "analysed": 2711, "with_documents": 2707, "with_office_actions": 2605, "with_filing_date": 2490, "three_year_cohort": 187 }, "volume": { "applications_analyzed": 2711, "tier": "large" }, "notes": ["percentile_rankings: not computed for this examiner (all three ranking tables were empty upstream)."] }, "claim_interpretation": null, "sections_available": ["behavioral_patterns", "rejection_analysis", "applications_with_interviews", "applications_with_rcex", "by_sequence_length", "document_code_breakdown"], "sections_included": [] } ``` ## Examination guidance The MPEP and the EPO Guidelines, the graph of how their sections cite each other, and the form paragraphs the USPTO pastes into office actions. ### GET /guidance/mpep/:sectionId One MPEP section with its rules and required examiner actions. Cost: metered · Cached `private, max-age=86400` · MCP tool: `get_mpep_section` One MPEP section, as an examiner is required to apply it. Beyond the section text, each record carries `keyRules` and `examinerActions`: what the section obliges, separated from the prose that obliges it. That is usually what a caller holding a rejection actually wants, since the argument is about whether the examiner did what the section required. Section ids carry dots and parentheses and the punctuation is part of the key, so `601.01(c)` is passed through exactly as written. **Path parameters** | Name | Type | Required | Description | |---|---|---|---| | `sectionId` | `string`, `^[0-9A-Za-z.()\-]{1,40}$` | yes | MPEP section id, e.g. `2106` or `601.01(c)`. Dots and parentheses are part of the id. | **Responses** | Status | Meaning | |---|---| | `200` | The section, with `content`, `keyRules`, `examinerActions`, `legalReferences`, `technologyDomains` and `enrichedSummary`. | | `400` `VALIDATION_ERROR` | sectionId is not a well-formed MPEP section id. | | `404` `NOT_FOUND` | No MPEP section carries that id. | | `503` `CORPUS_UNAVAILABLE` | The guidance dataset is not configured or not reachable. | **Example** ```bash curl -sS "$BASE/guidance/mpep/2106" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` **Example response** (abridged; values are illustrative) ```json { "sectionId": "2106", "title": "Patent Subject Matter Eligibility", "chapter": "2100", "topicCategory": "patentability", "keyRules": ["elided"], "examinerActions": ["elided"], "legalReferences": ["35 U.S.C. 101"] } ``` ### GET /guidance/epo/:sectionId One EPO Guidelines section with its rules and citations. Cost: metered · Cached `private, max-age=86400` · MCP tool: `get_epo_guideline` One section of the EPO Guidelines for Examination. Deliberately the same shape as the MPEP route, because the flow that reaches it is the same flow one step later: an applicant with a US rejection usually has a European family member, and an agent that learned one lookup should not have to learn a second. These records were merged from two source collections that each held half of each section, so a complete document carries both the generated half (`keyRules`, `examinerActions`) and the source half (`citations`, `sourceUrl`, `parentSection`, `childSections`). **Path parameters** | Name | Type | Required | Description | |---|---|---|---| | `sectionId` | `string`, `^[0-9A-Za-z.\-]{1,40}$` | yes | EPO Guidelines section id, e.g. `c-iv-2` or `g-ii-3.3`. | **Responses** | Status | Meaning | |---|---| | `200` | The section, with `content`, `keyRules`, `examinerActions`, `citations`, `parentSection`, `childSections` and `sourceUrl`. | | `400` `VALIDATION_ERROR` | sectionId is not a well-formed EPO section id. | | `404` `NOT_FOUND` | No EPO section carries that id. | | `503` `CORPUS_UNAVAILABLE` | The guidance dataset is not configured or not reachable. | **Example** ```bash curl -sS "$BASE/guidance/epo/g-ii-3.3" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` **Example response** (abridged; values are illustrative) ```json { "sectionId": "g-ii-3.3", "title": "Programs for computers", "part": "G", "chapter": "II", "keyRules": ["elided"], "citations": ["elided"], "sourceUrl": "https://www.epo.org/en/legal/guidelines-epc" } ``` ### GET /guidance/form-paragraph/:code One office-action form paragraph, with its statute and MPEP section. Cost: metered · Cached `private, max-age=86400` · MCP tool: `get_form_paragraph` The literal paragraph an examiner pasted into an office action. This is the sharpest thing in the dataset. A caller holding a rejection has a paragraph number and nothing else, and this turns it into the statute behind it, the MPEP section it rests on, the rejection type it belongs to, and the notes the examiner was given about when to use it. `brackets` lists the placeholders the examiner had to fill, which is often where the substance of a rejection actually sits. `isAIA` and `preAIAEquivalent` matter for anything filed near the changeover: citing the wrong one is citing law that does not apply to the application. The paragraph symbol is stripped, so a code copied straight out of a PDF as `¶ 6.11` resolves rather than returning a 404 that reads as "no such paragraph". **Path parameters** | Name | Type | Required | Description | |---|---|---|---| | `code` | `string`, `^[¶\s0-9A-Za-z.\-]{1,24}$` | yes | Form paragraph code, e.g. `6.11` or `7.37.01`. A leading `¶` is accepted and ignored. | **Responses** | Status | Meaning | |---|---| | `200` | The paragraph: `title` (its heading), `template` (the text as pasted, placeholders as `[1]`, `[2]`), `brackets`, `statute`, `statuteShort`, `rejectionType`, `category`, `isAIA`, `preAIAEquivalent`, `mpepSection`, `examinerNotes`, `mustFollow` (paragraphs that have to appear before this one) and `relatedParagraphs`. `truncated_at_source: true` means the stored text ends before the paragraph does; quote it as partial. `statute` and `rejectionType` are each null on a majority of rows, and on different rows. | | `400` `VALIDATION_ERROR` | code is not a well-formed form paragraph code. | | `404` `NOT_FOUND` | No form paragraph carries that code. | | `503` `CORPUS_UNAVAILABLE` | The guidance dataset is not configured or not reachable. | **Example** ```bash curl -sS "$BASE/guidance/form-paragraph/7.34.10" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` **Example response** (abridged; values are illustrative) ```json { "code": "7.34.10", "title": "Indefinite Claim Language: “Such As”", "template": "Regarding claim [1], the phrase “such as” renders the claim indefinite because it is unclear whether the limitations following the phrase are part of the claimed invention. See MPEP § 2173.05(d).", "statute": null, "rejectionType": "indefiniteness", "mpepSection": "2175", "examinerNotes": "Examiner Note: This form paragraph must be preceded by form paragraph 7.34.01.", "mustFollow": ["7.34.01"] } ``` ### GET /guidance/form-paragraphs Form paragraphs filtered by rejection type or MPEP section. Cost: metered · Cached `private, max-age=86400` · MCP tool: `find_form_paragraphs` Every form paragraph an examiner could have reached for. Given a rejection type, this is the set the examiner chose from, which is what an agent needs to tell "they picked the narrow one" from "this was the only option". Filtering by `mpepSection` answers the reverse: which paragraphs rest on a section you are already arguing about. At least one filter is required. An unfiltered call would page through everything to no purpose, so the caller has to say which axis they are on rather than receive the whole table by default. `rejectionType` takes either of the two vocabularies the corpus holds. The stored label is a word (`obviousness`, `indefiniteness`, `means_plus_function`) and the stored statute is a citation (`35 U.S.C. 103(a)`, `37 CFR 1.52`); each is null on a majority of rows, and on different rows. A query on either is expanded to both, so `112(b)` reaches the paragraphs labelled indefiniteness as well as the ones citing the statute, and `indefiniteness` reaches the same set. `filter.matched` shows the expansion. **Query parameters** | Name | Type | Required | Description | |---|---|---|---| | `rejectionType` | `string`, `^[0-9A-Za-z.()_ \-]{1,40}$` | no | A statute (`102`, `103`, `112(b)`, `101`, `121`, or a CFR rule such as `1.52`) or a label (`obviousness`, `anticipation`, `indefiniteness`, `enablement`, `written_description`, `means_plus_function`, `abstract_idea`, `double_patenting`, `restriction`, `allowance`, `drawing_objection`). Required unless `mpepSection` is given. | | `mpepSection` | `string`, `^[0-9A-Za-z.()\-]{1,40}$` | no | MPEP section the paragraph rests on, e.g. `706`, `707`, `2175`. Required unless `rejectionType` is given. | | `limit` | `integer`, 1–100 | no | How many paragraphs to return. Defaults to `25`. | **Responses** | Status | Meaning | |---|---| | `200` | `{ results[], count, filter }`. `filter` echoes what was applied and `filter.matched` what `rejectionType` expanded to; an unrecognised value returns zero with `filter.note` listing the vocabulary. | | `400` `VALIDATION_ERROR` | Neither filter was supplied, or one was malformed. | | `503` `CORPUS_UNAVAILABLE` | The guidance dataset is not configured or not reachable. | **Example** ```bash curl -sS "$BASE/guidance/form-paragraphs?rejectionType=112(b)&limit=5" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` **Example response** (abridged; values are illustrative) ```json { "results": ["one form paragraph per entry; elided here"], "count": 5, "filter": { "rejectionType": "112(b)", "matched": { "rejectionType": ["indefiniteness"], "statute": "35 U.S.C. 112(b)" } } } ``` ### GET /guidance/graph/:nodeId One-hop cross-references into and out of a guidance section. Cost: metered · Cached `private, max-age=86400` · MCP tool: `get_guidance_graph` What a guidance section points at, and what points at it. One hop, both directions, because they answer different questions. Forward is what else this rule depends on. Backward is what breaks if you misread it, and it is the direction that is hard to get any other way. Which graph is read off the node id rather than passed separately: ids are `mpep-601.01(c)` and `epo-c-ix-3`, so the prefix routes the query. An id with neither prefix is a 404 rather than a guess, because answering an EPO question with US law is worse than answering nothing. A hub section is cited by hundreds of others, so the fan-out is capped and `truncated` says which direction hit the cap. A caller seeing exactly `limit` edges cannot otherwise tell a hub from a coincidence. **Path parameters** | Name | Type | Required | Description | |---|---|---|---| | `nodeId` | `string`, `^(mpep|epo)-[0-9A-Za-z.()\-]{1,40}$` | yes | Graph node id. The prefix selects the corpus: `mpep-2106`, `epo-c-ix-3`. | **Query parameters** | Name | Type | Required | Description | |---|---|---|---| | `limit` | `integer`, 1–200 | no | Maximum edges per direction. Defaults to `50`. | **Responses** | Status | Meaning | |---|---| | `200` | `{ node, corpus, outgoing[], incoming[], truncated, limit }`. Each edge is `{ source, target, weight, type, bidirectional }`. | | `400` `VALIDATION_ERROR` | nodeId carries no recognised prefix, or limit is outside 1-200. | | `404` `NOT_FOUND` | No graph node carries that id. | | `503` `CORPUS_UNAVAILABLE` | The guidance dataset is not configured or not reachable. | **Example** ```bash curl -sS "$BASE/guidance/graph/mpep-2106?limit=10" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` **Example response** (abridged; values are illustrative) ```json { "node": { "nodeId": "mpep-2106", "inDegree": 214, "outDegree": 63, "isHub": true }, "corpus": "mpep", "outgoing": [{ "source": "mpep-2106", "target": "mpep-2106.04", "weight": 8, "type": "cites" }], "incoming": [{ "source": "mpep-2103", "target": "mpep-2106", "weight": 5, "type": "cites" }], "truncated": { "outgoing": false, "incoming": true }, "limit": 10 } ``` ### GET /guidance/design-code/:code One USPTO design search code. Cost: metered · Cached `private, max-age=86400` · MCP tool: `get_design_code` One USPTO design search code. A classification scheme rather than guidance. It sits in this namespace because it travelled with the same passover, not because it belongs beside the MPEP, and it is documented that way rather than filed somewhere tidier that would imply a relationship these records do not have. **Path parameters** | Name | Type | Required | Description | |---|---|---|---| | `code` | `string`, `^[0-9]{2}(\.[0-9]{2}){0,3}$` | yes | Design search code, e.g. `03.19.17`. | **Responses** | Status | Meaning | |---|---| | `200` | The code, with `descriptions[]` (every phrasing the USPTO files it under), `major` and `sub`. | | `400` `VALIDATION_ERROR` | code is not a design search code. | | `404` `NOT_FOUND` | No design code carries that value. | | `503` `CORPUS_UNAVAILABLE` | The guidance dataset is not configured or not reachable. | **Example** ```bash curl -sS "$BASE/guidance/design-code/03.19.17" \ -H "Authorization: Bearer $ANTECEDINT_API_KEY" ``` **Example response** (abridged; values are illustrative) ```json { "code": "03.19.17", "descriptions": ["Fish symbol used for religious significance", "Ichthys"], "major": "03", "sub": "03.19" } ``` # MCP server Endpoint: `POST https://mcp.antecedint.com/api/v1/mcp` Stateless Streamable HTTP, JSON-RPC 2.0. Protocol version 2025-06-18, also accepting 2025-03-26 and 2024-11-05. GET and DELETE answer 405. There is no session id to send or store. Bodies are capped at 2 MB. Batches are accepted for 2025-03-26 clients: up to 20 messages, of which at most 2 may be `tools/call`. Limits are 120 messages and 30 tool calls per minute, per key, on top of the daily and monthly request quotas that apply to REST. Client configuration: ```json { "mcpServers": { "antecedint": { "url": "https://mcp.antecedint.com/api/v1/mcp", "headers": { "Authorization": "Bearer ak_live_..." } } } } ``` ## Tools ### search_prior_art Prior Art Search. Semantic prior-art search over 10.5M+ US patents for a claim and/or abstract. Two-stage neural retrieval (fine-tuned dense encoder + optional late-interaction reranker) plus a citation-graph channel. Returns ranked references with titles, abstracts and similarity scores. Full claim text is NOT included by default: set includeClaims, or rank first and pull the few worth reading with get_patent_claims, which is the cheaper shape for most work. Specification bodies are never returned. Pass filedBefore with the priority date of the application you are searching for: prior art must predate the invention, and without that bound the results include later filings that cannot be cited. | Parameter | Type | Required | Description | |---|---|---|---| | `claims` | `string`, 10–20000 chars | no | Claim text (independent claim 1 works best). | | `abstract` | `string`, 0–10000 chars | no | Optional abstract or invention summary. | | `count` | `integer`, 1–100 | no | Number of references to return (default 50). | | `rerank` | `boolean` | no | Apply the ColBERT reranker to the top results (slower, higher precision). | | `filedBefore` | `string` | no | Exclude references filed on or after this date (YYYY, YYYY-MM or YYYY-MM-DD). Prior art must predate the invention it is cited against, so pass the priority date of the application being searched. Without it the results include later filings, which cannot be cited as prior art against it. | | `hybrid` | `boolean` | no | Also run keyword (BM25) search over titles and abstracts and fuse the rankings (default false). Set it when your query uses a term of art: the encoder was trained on meaning and does not guarantee an exact phrase outranks a paraphrase, so dense search alone can miss a document that names the thing. Each result gains matched_legs, how many of the three retrievers found it. | | `hyde` | `boolean` | no | Rewrite the query into patent language before searching (default false). A model drafts a hypothetical abstract and first claim for what you described, and that is searched instead. Set it when you have a description rather than claim text: the index holds formal claim language and a plain-English query has to cross that gap unaided. Do NOT set it when you already have a claim, which is the strongest query available. Adds about two seconds. metadata.hyde returns the generated text. | | `includeClaims` | `boolean` | no | Return full claim text for every reference (default false). The lookup adds about a second regardless of how many references are asked for, so leaving this off and calling get_patent_claims on the two or three worth reading is usually faster overall. While it is off, each result carries title_source: "namespace", meaning the title came from the vector index rather than the authoritative lookup and may name another patent. | | `cpc3` | `string[]` | no | Restrict to these CPC subclasses, e.g. ["H01M","F28D"]. Useful for a second pass once a first search has shown which art areas matter. CPC data covers about 65% of the index and an unclassified reference is excluded by this filter rather than ranked lower, so a first search should usually leave it unset. | ### get_patent_claims Patent Claim Text. Fetch full claim text and abstracts for specific US application numbers, up to 25 per call. Use after search_prior_art to read the references worth reading: the search ranks and this returns the text, so a search of fifty does not pay to fetch fifty full claim sets. Titles here come from the authoritative lookup table rather than the vector index. | Parameter | Type | Required | Description | |---|---|---|---| | `appNumbers` | `string[]`, max 25 | yes | US application numbers, digits only. These are the application_number values a search returns. | ### get_patent_neighborhood Patent Neighborhood. Given a US application number, return its position in Antecedint's semantic patent space plus its nearest semantic neighbors (similar patents with similarity scores). | Parameter | Type | Required | Description | |---|---|---|---| | `appNum` | `string`, `^\d{7,12}$` | yes | US application number, digits only (7-12 digits). | | `topK` | `integer`, 1–100 | no | Number of neighbors (default 20). | ### get_patent_citations Cited Prior Art Map. For a US application number, return its cited prior art (examiner-cited references flagged and listed first) resolved to patent-space positions. Useful for seeing what an examiner actually cited against an application. | Parameter | Type | Required | Description | |---|---|---|---| | `appNum` | `string`, `^\d{7,12}$` | yes | US application number, digits only. | | `year` | `integer`, 1970–2035 | no | Filing year of the citing application, if known (makes the lookup faster). | ### locate_in_patent_space Semantic Patent-Space Locate. Semantic search into patent space: give free text (or a US application number) and get the nearest patents with similarity scores plus a landing position. A fast way to find the closest existing art to any technical text. | Parameter | Type | Required | Description | |---|---|---|---| | `query` | `string`, 8–4000 chars | no | Free-text technical description to locate. Provide either query or appNum. | | `appNum` | `string`, `^\d{7,12}$` | no | US application number to locate by its own abstract + first claim. | | `topK` | `integer`, 1–100 | no | Number of nearest patents (default 20). | ### check_antecedent_basis Antecedent Basis Check. Check a claim set for §112(b) antecedent-basis defects. Finds definite recitations ("the widget", "said widget") with no earlier indefinite recitation in the same claim or in a claim it depends from, plus double inclusions, plural/singular mismatches, and improper dependencies. Implicit antecedent basis (MPEP 2173.05(e)) is suppressed, so "the outer surface of the housing" is not flagged. Findings are graded high/medium/low: high means no plausible antecedent exists anywhere, low means one probably exists under different wording. Deterministic, no LLM. Pass claims for a draft, or appNum for a filed application. | Parameter | Type | Required | Description | |---|---|---|---| | `claims` | `object[]`, max 300 | no | The claim set, in order. Provide either claims or appNum. | | `appNum` | `string`, `^\d{7,8}$` | no | US application number, digits only; pulls the claims from the warehouse. | | `minSeverity` | `string`, one of `high`, `medium`, `low` | no | Lowest severity to report (default low = everything). | | `includeSuppressed` | `boolean` | no | Also return candidates the implicit-basis rules suppressed, with the rule that killed each. Useful for auditing the checker. | ### map_claim_support Claim Term Support Map. Map every claim term to the specification passages that give it meaning, with exact locations (section, paragraph number, char offsets). Each term is graded by HOW the spec supports it: formally defined, defined by the claim, scoped by enumeration, mapped to an embodiment, characterized functionally, merely described, or UNDESCRIBED, which is a §112(a) written-description signal. With includeParents, the same analysis runs against the specs of the parent applications in the continuity chain (continuation, CIP, divisional, provisional), so terms supported here but in no ancestor are surfaced as NEW MATTER that cannot claim the parent's filing date. | Parameter | Type | Required | Description | |---|---|---|---| | `appNum` | `string`, `^\d{7,8}$` | no | US application number, digits only. Required for the continuity walk. | | `claims` | `object[]`, max 300 | no | Claim set to analyze. Omit to use the filed claims for appNum. | | `sections` | `object[]`, max 60 | no | Specification sections to ground against. Omit to pull the filed spec for appNum. | | `includeParents` | `boolean` | no | Walk the continuity chain and check each parent spec for support (default true when appNum is given). | | `maxAncestors` | `integer`, 0–12 | no | Cap on ancestors to check (default 6). | | `ocr` | `boolean` | no | Allow OCR for image-only filings. Most provisionals are image-only, so this is usually needed to read them. | ### find_whitespace Patent Whitespace Opportunities. List analyzed gaps ("whitespace") in patent space ranked by opportunity score. Each gap includes a summary of what is missing, bordering CPC areas and patents, temporal profile, and nearby active assignees. Filter by CPC section letter and minimum opportunity. | Parameter | Type | Required | Description | |---|---|---|---| | `limit` | `integer`, 1–500 | no | Max gaps to return (default 100). | | `section` | `string`, `^[A-HYa-hy]$` | no | CPC section letter filter (A-H, Y). | | `minOpportunity` | `number`, 0–1 | no | Minimum opportunity score (0-1). | ### lookup_cpc_code CPC Code Lookup. Look up a CPC classification symbol: what it covers, where it sits in the scheme, and which art units actually examine it. The art-unit spread is empirical (where applications carrying the symbol were really routed) with per-unit probabilities and an entropy score: low entropy means the symbol predicts the art unit, high means it does not. Use before filing to anticipate routing, or to decide whether classification is worth arguing. | Parameter | Type | Required | Description | |---|---|---|---| | `code` | `string`, `^[A-HYa-hy][0-9A-Za-z/]{0,18}$` | yes | CPC symbol, e.g. C11D or C11D7/261. | | `childLimit` | `integer`, 0–500 | no | How many child symbols to return (default 50). | | `distributionLimit` | `integer`, 0–500 | no | How many art units to list in the distribution, largest first (default 20). unique_art_units is always the true total. | ### get_patent_classification Patent CPC Classification. Get the CPC symbols assigned to a published application or granted patent, from the USPTO Master Classification File. Separates inventive symbols (covering what is claimed) from additional ones (disclosed but not claimed) — build a prior-art search from the inventive set. Accepts a publication or application number. | Parameter | Type | Required | Description | |---|---|---|---| | `number` | `string`, `^\d{7,12}$` | yes | Publication or US application number, digits only. | ### get_art_unit_profile Art Unit Profile. Profile one USPTO art unit: what technology it examines, its examiner roster, and how it behaves — allowance and rejection rates, time to first action, interview receptiveness, RCE tolerance and estimated prosecution cost. generated_context is the one-paragraph account of the technology; the model-written long description is served as its opening and length only. examiner_count is the roster length, and examiner_count_in_statistics appears when the stored statistics counted a different roster. Check statistics.data_quality before relying on the numbers: it reports how many applications and office actions they rest on, and thin units carry the same field names with far weaker support. | Parameter | Type | Required | Description | |---|---|---|---| | `artUnit` | `string`, `^\d{3,4}$` | yes | USPTO art unit, e.g. 2872. | ### find_examiner Examiner Search. Find USPTO examiners by surname or art unit, returning ids and names to pass to get_examiner_profile. Matching is anchored, so pass a surname (or the start of the surname-first name), not a substring from the middle. | Parameter | Type | Required | Description | |---|---|---|---| | `name` | `string`, 2–80 chars | no | Surname, or a prefix of the surname-first full name. | | `artUnit` | `string`, `^\d{3,4}$` | no | Restrict to one art unit. | | `limit` | `integer`, 1–100 | no | Maximum matches (default 20). | ### get_examiner_profile Examiner Statistics. Statistics for one USPTO examiner, by id or by surname-first name (e.g. "SMITH ADAM M"): rejection analytics, allowance behaviour, interview and RCE patterns, timing and cost. Use it to calibrate a response strategy to the examiner who will actually read it. The default response is about 4,000 tokens: per-application lists (applications_with_interviews, applications_with_rcex), two wide histograms (by_sequence_length, document_code_breakdown) and two cold per-application sections (behavioral_patterns, rejection_analysis) are each replaced by a count and the include name that brings them back. percentile_rankings and claims_analysis are null where never computed, which is every examiner sampled. Read data_quality first: populations names the denominators, volume.tier says how much the numbers rest on, withheld lists fields removed as wrong, and notes lists pairs that disagree. | Parameter | Type | Required | Description | |---|---|---|---| | `examiner` | `string`, 2–120 chars | yes | Examiner id, or USPTO surname-first full name. | | `include` | `string` | no | Comma-separated sections to add: applications_with_interviews, applications_with_rcex, by_sequence_length, document_code_breakdown (thousands of tokens each); behavioral_patterns, rejection_analysis (cold, may reach megabytes). | ### warm_up Warm the Inference Hub. Start the encoder before you need it. The inference hub scales to zero, so the first search_prior_art or locate_in_patent_space after an idle period waits about 40 seconds for models to load. Call this first and that wait happens outside your search. Returns immediately by default with an estimate; pass wait to block until the models are ready instead of polling. Free, and it never counts against quota. Set rerank only if the search will set rerank, since the reranker costs another 25 seconds to load. | Parameter | Type | Required | Description | |---|---|---|---| | `rerank` | `boolean` | no | Also load the ColBERT reranker. Only needed if the search that follows sets rerank; it adds about 25 seconds to the warm-up. | | `wait` | `integer`, 0–120 | no | Seconds to block until the models are ready, up to 120. The default of 0 returns straight away with an estimate, which suits warming ahead of other work. Set it to turn a polling loop into one call. | ### get_mpep_section MPEP Section. One MPEP section, with keyRules and examinerActions separated from the prose that obliges them. This is what a US examiner is required to apply, so it is the text an argument about whether they applied it correctly has to rest on. Section ids carry dots and parentheses (601.01(c)) and the punctuation is part of the id. | Parameter | Type | Required | Description | |---|---|---|---| | `sectionId` | `string`, `^[0-9A-Za-z.()\-]{1,40}$` | yes | MPEP section id, e.g. 2106 or 601.01(c). | ### get_epo_guideline EPO Guidelines Section. One section of the EPO Guidelines for Examination, in the same shape as get_mpep_section because the flow that reaches it is the same flow one step later: an applicant with a US rejection usually has a European family member. Carries citations, parentSection and childSections alongside the generated keyRules. | Parameter | Type | Required | Description | |---|---|---|---| | `sectionId` | `string`, `^[0-9A-Za-z.\-]{1,40}$` | yes | EPO Guidelines section id, e.g. g-ii-3.3 or c-iv-2. | ### get_form_paragraph Office Action Form Paragraph. The literal paragraph an examiner pasted into an office action, by its code. Turns a paragraph number read off a rejection into the statute behind it, the MPEP section it rests on, the rejection type, and the notes the examiner was given about when to use it. title is the heading and template the text with [1], [2] placeholders; brackets lists what fills them, which is often where the substance of the rejection sits. mustFollow names paragraphs that have to appear before this one. isAIA and preAIAEquivalent matter for anything filed near the changeover. A leading paragraph symbol is accepted. | Parameter | Type | Required | Description | |---|---|---|---| | `code` | `string`, `^[¶\s0-9A-Za-z.\-]{1,24}$` | yes | Form paragraph code, e.g. 6.11 or 7.37.01. | ### find_form_paragraphs Form Paragraphs by Rejection Type. Every form paragraph an examiner could have reached for, filtered by rejection type or by the MPEP section it rests on. Given a rejection type this is the set they chose from, which is what tells a deliberately narrow choice from the only available one. rejectionType takes a statute (103, 112(b), 101, 1.52) or a label (obviousness, indefiniteness, enablement, double_patenting); either reaches the paragraphs the other would. At least one filter is required; this does not list the whole table. | Parameter | Type | Required | Description | |---|---|---|---| | `rejectionType` | `string`, `^[0-9A-Za-z.()_ \-]{1,40}$` | no | A statute (102, 103, 112(b), 112(a), 112(f), 101, 121, or a CFR rule such as 1.52) or a label (obviousness, anticipation, indefiniteness, enablement, written_description, means_plus_function, abstract_idea, double_patenting, restriction, allowance, drawing_objection). | | `mpepSection` | `string`, `^[0-9A-Za-z.()\-]{1,40}$` | no | MPEP section the paragraph rests on, e.g. 706, 707 or 2175. | | `limit` | `integer`, 1–100 | no | How many to return (default 25). | ### get_guidance_graph Guidance Cross-References. What a guidance section points at and what points at it, one hop, both directions. Forward is what else a rule depends on; backward is what breaks if you misread it, and is the direction that is hard to get any other way. The node id prefix selects the corpus: mpep-2106 or epo-c-ix-3. A hub section is cited by hundreds of others, so the fan-out is capped and truncated says which direction hit the cap. | Parameter | Type | Required | Description | |---|---|---|---| | `nodeId` | `string`, `^(mpep|epo)-[0-9A-Za-z.()\-]{1,40}$` | yes | Graph node id, prefixed mpep- or epo-. | | `limit` | `integer`, 1–200 | no | Maximum edges per direction (default 50). | ### get_design_code USPTO Design Search Code. One USPTO design search code, e.g. 03.19.17 for a fish symbol used for religious significance. A classification scheme rather than guidance; it sits in this corpus because it arrived with the same data, not because it belongs beside the MPEP. | Parameter | Type | Required | Description | |---|---|---|---| | `code` | `string`, `^[0-9]{2}(\.[0-9]{2}){0,3}$` | yes | Design search code, e.g. 03.19.17. |