# patent-api for agents

You are an AI agent. This page tells you how to get an API key, get it funded by your human, and query
the status of any US patent application. No human is needed except to pay. The base URL is filled in below.

What you get: for every US patent application — is it pending, rejected, allowed, abandoned, granted; the
transaction timeline; who owns it (assignments); PTAB challenges and what the Board decided; maintenance fees / is the patent alive;
and resolved entity ids for companies and inventors. Built from USPTO's public-domain bulk data, served from
our own copy. Every response carrying USPTO data carries `source` (the USPTO product and file date) and `as_of`;
your own objects — keys, balance, monitors — have no USPTO file behind them and carry neither.

**No seats, redistribution allowed, no AI-use restrictions.** Cache it, embed it, store it in a vector
database, resell it inside your product. Pay per request only.

## 1. Sign up (needs your owner's real email)

```bash
curl -s -X POST https://api.patentdata.ai/agent/signup \
  -H 'Content-Type: application/json' \
  -d '{"owner_email": "owner@example.com", "agent_name": "my-patent-watcher"}'
```

```json
{"api_key": "pd_live_...", "org_id": "org_...", "note": "save this now; not shown again"}
```

Save `api_key` immediately: it is never shown again. Your owner receives an email saying an agent enrolled
under their address (the email never contains the key). Rules:

- Lost the key before funding? Call signup again with the same email: the old key is revoked and a new one
  is minted.
- Once the org has ever been funded, signup with that email returns `409 {"error": "org_exists"}`. Mint more
  keys with `POST /keys` (step 5) using a key you still have.

## 2. You will get a 402 once the trial is spent

Every new org starts with a small trial allowance (see `balance_units` in `GET /agent/me`) so your first calls
return data. After that, any data request on an unfunded key returns:

```json
{"error": "payment_required", "checkout": "POST /agent/checkout-link", "minimum_usd": 20}
```

This is the onboarding flow, not a failure. Go to step 3.

## 3. Get a checkout link and hand it to your human

```bash
curl -s -X POST https://api.patentdata.ai/agent/checkout-link \
  -H 'Content-Type: application/json' -H 'X-API-KEY: pd_live_...' \
  -d '{"amount_usd": 20}'
```

```json
{"url": "https://checkout.stripe.com/c/pay/cs_...", "expires_at": "2026-09-14T17:00:00+00:00"}
```

Give `url` to your owner. They pay on Stripe's hosted page, no login. Credits land on your key within
seconds of the payment confirming. Poll `GET /agent/me` until `balance_units` is positive. Whole dollars,
minimum 20; the link expires after 24 hours, just request another.

If the service answers `503 {"error": "billing_not_configured"}`, billing is not enabled on this deployment
yet; tell your owner.

## 4. Query

```bash
curl -s https://api.patentdata.ai/v1/applications/17123456 -H 'X-API-KEY: pd_live_...'      # by application number
curl -s https://api.patentdata.ai/v1/patents/11987654 -H 'X-API-KEY: pd_live_...'            # by patent number
curl -s https://api.patentdata.ai/v1/publications/US20230080435A1 -H 'X-API-KEY: pd_live_...' # by publication number
curl -s -X POST https://api.patentdata.ai/v1/applications/status -H 'X-API-KEY: pd_live_...' \
     -H 'Content-Type: application/json' -d '{"application_numbers": ["17123456", "17000002"]}'   # up to 1,000 per call
curl -s 'https://api.patentdata.ai/v1/events?application_numbers=17123456&since=2026-09-01' -H 'X-API-KEY: pd_live_...'  # what happened
curl -s https://api.patentdata.ai/v1/entities/ent_c54d3fb3c2cf593ba26a/stats -H 'X-API-KEY: pd_live_...'  # what is this company doing: portfolio, velocity, prosecution, CPC, examiners, PTAB, deals
curl -s 'https://api.patentdata.ai/v1/examiners/SMITH,%20JANE/stats' -H 'X-API-KEY: pd_live_...'   # examiner allowance rate, time to first action, RCE/appeal rates
curl -s https://api.patentdata.ai/v1/art-units/2857/stats -H 'X-API-KEY: pd_live_...'              # the same for an art unit
curl -s https://api.patentdata.ai/v1/patents/11987654/claims -H 'X-API-KEY: pd_live_...'         # claims, abstract, CPC of a grant
curl -s https://api.patentdata.ai/v1/publications/US20230080435A1/claims -H 'X-API-KEY: pd_live_...'  # same for a published application
curl -s 'https://api.patentdata.ai/v1/patents/11987654/citations?direction=both' -H 'X-API-KEY: pd_live_...'  # front-page prior art (backward) and citing grants (forward, paginated)
curl -s -G https://api.patentdata.ai/v1/search --data-urlencode 'q="lipid nanoparticle" mRNA -virus' -d cpc=C12N -d doc_type=grant -d filed_after=2020-01-01 \
     -H 'X-API-KEY: pd_live_...'   # keyword search over title/abstract/claims, one CPC level, filing-date window; add entity_id= for one owner
curl -s https://api.patentdata.ai/v1/patents/11987654/ptab -H 'X-API-KEY: pd_live_...'           # PTAB challenges: instituted?, claims_outcome, every Board decision
curl -s 'https://api.patentdata.ai/v1/ptab/IPR2026-00047?include_text=true' -H 'X-API-KEY: pd_live_...'  # one trial with each decision's text preview and PDF link
```

The application object's `applicant_name` says who filed; `current_owner` (`{name, entity_id, recorded_date,
reel_frame, co_owners}`, null when nothing was recorded) says who holds title per the USPTO assignment record,
and `/v1/entities/{id}/applications|patents` list what an entity owns today (`role: "owner"`) or, absent any
recorded transfer, what it filed (`role: "applicant"`). A company printed under several names has several entity ids;
entity search folds them into one row with a `fam_...` id and `members`, and a family id works wherever an entity id
does — use it for the whole company, an entity id (each carries its `family_id`) for one name. `/v1/entities/{id}/stats` sums that portfolio up — counts,
filings and grants per year, allowance rate and months to grant (computed the same way as an examiner's, so the fields both carry compare
directly — the windows differ, so read each `computed_over`),
top CPC subclasses with `cpc_coverage`, top examiners and customer numbers, PTAB proceedings as owner and petitioner,
properties acquired and sold in the last three years — with a `definitions` block that says how each number was made.
Large companies are precomputed nightly; smaller ones compute on your first call. `504 computation_timeout` means
the compute ran over its budget and the entity is queued for the next nightly run. `503 not_computed_yet` means the
nightly has not written that company's numbers yet — or a correction retired them and it is rewriting them — and
`Retry-After` says when it runs; `/health` counts what is waiting under `recomputing`.

Every successful data request costs 1 unit; list and batch endpoints cost 1 unit per 100 rows returned, minimum 1
(an empty result still cost a query). Managing your own account is free: creating, listing and deleting monitors,
reading their event log, `/agent/me`, `/keys`. You pay for USPTO data and for each webhook delivery. Poll `/v1/events` for changes or create a webhook monitor with
`POST /v1/monitors`; both use these event types: office_action, allowance, abandonment, rce, appeal, issue_fee_paid, grant, publication, response_filed, interview, terminal_disclaimer, pta, status_change, assignment, ptab_petition, ptab_decision, maintenance_due, maintenance_lapse, continuation_filed. A transaction row's `event_type` is the timeline
vocabulary (office_action, allowance, abandonment, rce, appeal, issue_fee_paid, grant, publication, response_filed, interview, terminal_disclaimer, pta), which the feed's list contains: every type the feed emits can be filtered
for and put in a monitor. The
feed's `since` is the *detection* time (`detected_at`, when our copy learned of it), not when it happened
(`occurred_on`); `coverage_since` on the response says when detection began — before it, nothing is in the feed,
which is not the same as nothing having happened. A monitor body is
`{"targets": {"application_numbers": [...], "patent_numbers": [...], "entity_ids": [...]}, "events": [...], "webhook_url": "https://..."}`.
See `https://api.patentdata.ai/openapi.json` for all endpoints.

`GET /v1/search` finds documents by the words you give (websearch syntax: `"quoted phrase"`, `-excluded`, `or`), within one
CPC level (`C`, `C12`, `C12N`, `C12N15`, `C12N15/88`), one owner (`entity_id`) and a filing-date window; results are ranked
with a `snippet`, `total` is capped (`total_capped`), and a query past its time budget answers `504 computation_timeout`
— add a CPC level, `doc_type` or a date range. It is keyword search, not similarity search.

When entity search does not resolve a name, `GET /v1/assignments?assignee=<name>` (and `?assignor=`) lists recordations
by raw party name; the application numbers on them feed `POST /v1/applications/status`. `counts.office_actions` on the
application object counts actions in the transaction history; `/office-actions` lists the ones whose text is on file.
`publication_number` null means USPTO's record carries none: the application was not published yet, or it was filed
with a non-publication request and never will be — the data does not distinguish the two.
`source` on an object names the USPTO product and file the *row itself* came from (an application loaded from a
snapshot says `PTFWPRE`; one touched by a daily delta says `PTFWPRD`); `as_of` and `stale` describe the table's
newest ingest. The short index of everything here is `https://api.patentdata.ai/llms.txt`. `/citations?direction=both`
returns two lists and is priced as two list calls.
On a PTAB proceeding, `decisions` lists the Board decision documents USPTO's trial-decisions dataset carries — that
dataset omits some trials — so `instituted`, `final_decision_date` and `final_written_decision` come from those
documents when present and from the trial's `status` otherwise; `claims_outcome` is null without a decision document,
and also when the final written decision's title does not state the verdict (USPTO's dataset carries the title, a
short text preview and the PDF link — read the PDF for the holding). `petitioner` / `patent_owner` are USPTO's
strings and USPTO leaves the owner's name off some proceedings; `patent_owner_entity_id` is then filled from the
challenged patent's current owner when that patent has exactly one — a co-owned patent has no single answer, so
both stay null and `/v1/patents/{n}` is where the owners are.
USPTO sets an application's status to "Patented Case" before the patent number reaches the file wrapper, so a
just-granted application can carry `status.category: "granted"` with `patent_number` and `grant_date` still null
for a few days; `portfolio.patents` can then be lower than `portfolio.granted`.

An office action carries `statutes` (every statute USPTO's own `rejections` array or `sections` keys name — the two
disagree on some actions, so both are read), `rejections` verbatim from the bulk product — one row per statute USPTO
recorded, not per rejection the examiner wrote, and `position` is its character offset into the action text, which
USPTO does not always place at the rejection (read `body_text` for the rejections themselves; no source gives them
by claim), `sections` from that same
bulk product, which lower-cases its text, and `body_text`, the letter as USPTO's API returns it, case intact. Both
hold the examiner's prose, so both need `include_text=true`; by default they are absent and `text_chars` /
`section_chars` say how much asking would cost.
Full text (`/claims`) covers a bounded window of grants and publications — a 404 there names the dates covered;
a claim that depends on an embedded image shows it as `[chemical structure]`, `[formula]`, `[table]` or `[image]`.
`counts.cited_by` counts the loaded grants citing the application's patent number; `/v1/patents/{n}/citations` lists
them (`forward`) and the patent's own front-page references (`backward`, null with a `detail` when that grant's weekly
file is not loaded).

## 5. Manage your org

```bash
# who am I, what is my balance, which keys exist
curl -s https://api.patentdata.ai/agent/me -H 'X-API-KEY: pd_live_...'
# mint another key on the same balance (e.g. one per deployment)
curl -s -X POST https://api.patentdata.ai/keys -H 'Content-Type: application/json' -H 'X-API-KEY: pd_live_...' \
  -d '{"name": "ci"}'
```

`GET /agent/me` returns `{"org_id", "owner_email", "balance_units", "keys": [{"key_id", "prefix", "name", "created_at"}]}`.
Any key under the org can top up the balance with `POST /agent/checkout-link`.

## Pricing

| Plan    | Price              | Requests                     | Notes                                                  |
| ------- | ------------------ | ---------------------------- | ------------------------------------------------------ |
| Free    | $0                 | 50 on signup                 | No card. Once per person.                              |
| Starter | $20 one-time       | 1,000 ($0.02/req)            | No expiry. Buy any whole-dollar amount ≥ $20.          |
| Scale   | volume pricing     | millions                     | Email rish@patentdata.ai: SLA, bulk exports, ZDR.      |

No seats, redistribution allowed, no AI-use restrictions.

## MCP

The same data is exposed as MCP tools (streamable HTTP) at `https://api.patentdata.ai/mcp`; authenticate with
your API key as the bearer token.

```bash
claude mcp add --transport http patent-data https://api.patentdata.ai/mcp --header "Authorization: Bearer <api_key>"
```

## Errors you will see

| Status | `error`                  | What to do                                                   |
| ------ | ------------------------ | ------------------------------------------------------------ |
| 401    | `missing_api_key` / `invalid_api_key` | No key header at all / a revoked or mistyped key. Send `X-API-KEY` or `Authorization: Bearer`. |
| 402    | `payment_required`       | Request a checkout link (step 3).                            |
| 400    | `amount_below_minimum`   | Ask for at least `minimum_usd`.                              |
| 409    | `org_exists`             | Email already owns a funded org; use `POST /keys` with an existing key. |
| 503    | `billing_not_configured` | Deployment has no Stripe keys; tell your owner.              |
| 504    | `computation_timeout`    | The request ran past its time budget; `detail` says what to narrow, or that the work is queued for the nightly run. Not charged. |
| 503    | `not_computed_yet`       | A precomputed answer is not ready (a large entity's statistics after a correction). `Retry-After` says when the job that writes it runs; `/health` counts what is waiting. |
| 400    | `bad_request`            | `detail` is a sentence: a malformed identifier, an unknown or misspelled query parameter (it lists the accepted ones), contradictory filters, an over-long `q`. |
| 400    | `invalid_cursor`         | The cursor did not come from this API, or was edited. Start the list again. |
| 404    | `not_found`              | A well-formed identifier this API does not hold. The body names the field and the value; `detail`, when present, says why (a coverage window). |
| 405    | `method_not_allowed`     | Wrong verb; the `Allow` header lists the right ones. |
| 422    | `validation_error`       | A parameter failed its type or bounds. Here alone `detail` is an ARRAY of FastAPI field errors, not a sentence. |
| 400    | `unknown_event_type` / `no_targets` | Creating a monitor: `allowed` lists the event types; a monitor needs at least one target. |
| 503    | `database_unavailable`   | Our database is not reachable. Retry; if it persists the service is down. |
| 500    | `internal_error`         | Our bug. The `request_id` identifies it — send it to us. |

Every response carries `X-Credits-Charged` — what this call cost, `0` on any error — so you never have to call
`/agent/me` to reconcile spend; a charged response also carries `X-Credits-Remaining`, your balance after it.

Source data: USPTO Open Data Portal bulk products (US government work, public domain). We cite USPTO; cite us.
Entity identity (`ent_...`) is seeded from PatentsView's disambiguated assignees — `source: "patentsview"` on an
entity says so, and `patentsview_id` is their id; names PatentsView never saw are resolved by us (`source: "ours"`).
Families (`fam_...`) are ours. Everything else comes from the USPTO products each response names.


# Patent Status API — endpoint reference (from /openapi.json)

## GET /health
**Service and data freshness**

## GET /v1/patents/{patent_number}
**Current status by patent number**

Answers: what is the state of the application behind this granted patent — same object as /v1/applications/{n}, looked up by patent number (the number printed on products and in Google Patents). 404 not_found if the patent is unknown.

Parameters:
- `patent_number` [path] (required): US patent number without commas; design/plant/reissue prefixes allowed (D912345, PP12345, RE48123).

## GET /v1/patents/{patent_number}/claims
**Claims, abstract and CPC of a granted patent**

Answers: what does this granted patent actually claim, what is it about, and how is it classified. Returns `claims` [{number, text, dependent_on}] (dependent_on lists the claims a dependent claim refers to; empty for independent claims; `canceled: true` marks claims canceled by amendment, `number_raw` flags a claim USPTO mis-numbered), `independent_claims`, `claim_count`, `canceled_claims`, `abstract`, `cpc` (CPC symbols as classified at issue, main first), `title`, `kind`, `application_number`, `date`. Text comes from USPTO's weekly full-text XML; a number outside the weeks we hold is a 404 whose `detail` states the covered dates — the status endpoints cover every application regardless. Costs 1 request.

Parameters:
- `patent_number` [path] (required): US patent number without commas; design/plant/reissue prefixes allowed (D912345, PP12345, RE48123).

## GET /v1/patents/{patent_number}/citations
**What a patent cites, and who cites it**

Answers: what prior art is on this patent's front page, and which later patents cite it. `backward` is the front-page list grouped by what was cited — `patents` (US, canonical numbers), `publications` (US pre-grant, US20130157853A1), `foreign` (country + number as printed), `npl` (non-patent literature text) — each with `category` (examiner | applicant | other, with `category_detail` for other) and the cited document's `date` at the precision USPTO prints (usually a month), plus `count` / `examiner_count` / `applicant_count`. `forward` is every loaded grant citing this patent number, newest number first: [{patent_number, grant_date, title, categories}] with `count` (distinct citing grants) and `next_cursor` (famous patents have thousands; page with `limit`/`cursor`). A reference USPTO printed unreadably is kept verbatim with a `defect` note. `direction` = both | backward | forward. Coverage is the weekly grant files loaded: a `forward.count` is complete for those weeks only, and `backward` is null with `detail` naming the covered dates when the patent's own grant file is not loaded. Citations of the patent's pre-grant publication are not folded in. Costs 1 request per 100 citation rows returned.

Parameters:
- `patent_number` [path] (required): US patent number without commas; design/plant/reissue prefixes allowed (D912345, PP12345, RE48123).
- `direction` [query]: both | backward | forward.
- `limit` [query]: Page size. Each 100 rows returned costs 1 request unit.
- `cursor` [query]: Opaque cursor from a previous response's next_cursor. Omit for the first page.

## GET /v1/publications/{publication_number}/claims
**Claims, abstract and CPC of a pre-grant publication**

Answers: what does this published application (as published, not as later amended) actually claim, what is it about, and how is it classified. Returns `claims` [{number, text, dependent_on}] (dependent_on lists the claims a dependent claim refers to; empty for independent claims; `canceled: true` marks claims canceled by amendment, `number_raw` flags a claim USPTO mis-numbered), `independent_claims`, `claim_count`, `canceled_claims`, `abstract`, `cpc` (CPC symbols as classified at publication, main first), `title`, `kind`, `application_number`, `date`. Text comes from USPTO's weekly full-text XML; a number outside the weeks we hold is a 404 whose `detail` states the covered dates — the status endpoints cover every application regardless. Costs 1 request. A bare number (2023/0080435) returns the latest kind on file (A9 corrected over A2 over A1).

Parameters:
- `publication_number` [path] (required): Pre-grant publication number, e.g. US20230080435A1 or 2023/0080435.

## GET /v1/publications/{publication_number}
**Current status by pre-grant publication number**

Answers: what is the state of the application behind this published application — same object as /v1/applications/{n}, looked up by publication number as printed (US20230080435A1) or bare (2023/0080435).

Parameters:
- `publication_number` [path] (required): Pre-grant publication number, e.g. US20230080435A1 or 2023/0080435.

## POST /v1/applications/status
**Status of many applications in one call**

Answers: what is the status of each of these applications — the `status` block, patent/publication numbers and applicant for up to 1,000 application numbers, in request order; a number we do not hold comes back in `not_found`, one that cannot be an application number in `invalid`, as you wrote it. Costs 1 request per 100 rows returned. Use /v1/applications/{n} for the full object.

## GET /v1/events
**What happened, as a feed you can poll**

Answers: what happened to these applications / patents / entities since I last looked — office actions, allowances, abandonments, grants, publications, assignments, PTAB petitions, maintenance events — as detected by our nightly ingest. Filter by `types` and/or targets (at least one filter is required); page with `cursor`; use `since` (ISO date or datetime, on detected_at) for 'since my last poll'. The webhook alternative is POST /v1/monitors. Costs 1 request per 100 rows.

Parameters:
- `types` [query]: Comma-separated event types, e.g. office_action,allowance,grant.
- `application_numbers` [query]: Comma-separated application numbers (no thousands separators: 17123456).
- `patent_numbers` [query]: Comma-separated patent numbers (no thousands separators: 11987654).
- `entity_ids` [query]: Comma-separated entity or family ids from /v1/entities/search.
- `since` [query]: Only events detected on/after this ISO date or datetime.
- `limit` [query]: Page size. Each 100 rows returned costs 1 request unit.
- `cursor` [query]: Opaque cursor from a previous response's next_cursor. Omit for the first page.

## GET /v1/applications/{application_number}/transactions
**Prosecution timeline of one application**

Answers: what has happened to this application and when — every USPTO transaction (filing, office actions, responses, notice of allowance, issue, abandonment), newest first. Each item is {date, code, description, event_type} where code/description are USPTO's verbatim and event_type is our normalized enum (office_action, allowance, abandonment, grant, publication, ... or null). Pass `since=YYYY-MM-DD` to get only what happened on or after a date (e.g. since your last check).

Parameters:
- `application_number` [path] (required): US application number, 8 digits. Slashes, commas and spaces are ignored ('17/123,456' = '17123456'), URL-encoded or not.
- `since` [query]: Only transactions dated on or after this ISO date.

## GET /v1/applications/{application_number}/office-actions
**Office actions (examiner rejections) of one application, with text**

Answers: what did the examiner reject, under which statutes, and when is a response due. Each item is one office action (non-final, final, restriction, ...) with mailed_date, the reply deadline per 37 CFR 1.134 / MPEP 710.02(b) (`response_due_estimate`, `response_due_max` with extensions under 1.136(a), `response_period_months`, `response_extendable`, `restarts_period` — all null for an action whose period is set in the letter; on a notice of allowance the estimate is the issue-fee due date), `statutes` and `rejections` when USPTO's parsed data exists — `statutes` carries the bare section beside any paragraphed form (103, 112, 112(a)) so a filter on '103' never misses, and is null rather than empty when USPTO sent no structured rejections at all, which is not the same as an action that rejected nothing. The examiner's prose — `body_text` and the per-statute rejection text inside `sections` — costs include_text=true (tens of KB each); without it `text_chars` and `section_chars` say how much there is to ask for. Text lags mailing by 0–7 days; a transaction can exist before its text does.

Parameters:
- `application_number` [path] (required): US application number, 8 digits. Slashes, commas and spaces are ignored ('17/123,456' = '17123456'), URL-encoded or not.
- `include_text` [query]: Include the full text of each action.

## GET /v1/applications/{application_number}/assignments
**Ownership records of one application**

Answers: who owns this application, and how did ownership change — every recorded assignment (reel/frame) whose properties include this application or the patent granted on it, newest recorded first. Each has `assignors`, `assignees` (with `entity_id` when resolved), `conveyance_type` (assignment, security interest, merger, ...), `recorded_date`, `execution_date`, and the `properties` covered by that recordation.

Parameters:
- `application_number` [path] (required): US application number, 8 digits. Slashes, commas and spaces are ignored ('17/123,456' = '17123456'), URL-encoded or not.

## GET /v1/applications/{application_number}
**Current status of one application**

Answers: what is the current state of this US patent application — pending, allowed, abandoned, or granted (and as which patent), who filed it, who examines it, what it claims priority to, and how much activity (office actions, assignments, PTAB challenges) it has seen. One object: `status` {code, description, category, date}, `patent_number`/`grant_date` (null until granted), `publication_number`/`publication_date`, `applicant_name` + `applicant_entity_id` (use with /v1/entities), `examiner_name`, `art_unit`, `entity_status` (small|micro|undiscounted), `continuity` {parents, children}, `counts`, and provenance `source` {product, file_date} / `as_of` / `stale`. For the timeline call /transactions; for ownership call /assignments. 404 not_found if we have no record.

Parameters:
- `application_number` [path] (required): US application number, 8 digits. Slashes, commas and spaces are ignored ('17/123,456' = '17123456'), URL-encoded or not.

## GET /v1/patents/{patent_number}/assignments
**Ownership records of one patent**

Same as /v1/applications/{n}/assignments but keyed by patent number: who owns this patent and every recorded transfer or security interest against it.

Parameters:
- `patent_number` [path] (required): US patent number without commas; design/plant/reissue prefixes allowed (D912345, PP12345, RE48123).

## GET /v1/assignments
**Assignments received by an assignee**

Answers: what patents/applications has this company acquired or been assigned, and when. `assignee` is an entity id (ent_...) or family id (fam_...) from /v1/entities/search, or a name (matched as a normalized prefix, so 'Impossible Foods' finds 'IMPOSSIBLE FOODS INC.'). `recorded_after` narrows to recordations after a date — use it to watch for new acquisitions. Paginated newest first with `next_cursor`. Costs 1 unit per 100 rows.

Parameters:
- `assignee` [query] (required): Entity id (ent_...), family id (fam_...) or assignee name.
- `recorded_after` [query]: Only recordations strictly after this ISO date.
- `limit` [query]: Page size. Each 100 rows returned costs 1 request unit.
- `cursor` [query]: Opaque cursor from a previous response's next_cursor. Omit for the first page.

## GET /v1/patents/{patent_number}/ptab
**PTAB challenges against one patent**

Answers: is this patent under (or has it survived) an inter partes review, post-grant review, CBM or derivation proceeding at the PTAB, and what did the Board decide. Each proceeding has `type`, `petitioner`, `patent_owner`, `filed_date`, `status`, `instituted` (true|false|null), `claims_outcome` (all_unpatentable|some_unpatentable|none_unpatentable|null, from the final written decision), `final_decision_date`, and `decisions` — every Board decision document with `kind` (institution_granted, institution_denied, final_written_decision, termination, rehearing, appeal_mandate, other), `date`, `outcome`, `statutes`, `issue_types`, `title`, `pdf_url`. Pass include_text=true for each decision's text as USPTO's dataset carries it (a preview of a few hundred characters). Empty `proceedings` for a known patent means no challenge on record.

Parameters:
- `patent_number` [path] (required): US patent number without commas; design/plant/reissue prefixes allowed (D912345, PP12345, RE48123).
- `include_text` [query]: Include the full text of each decision.

## GET /v1/ptab/{trial_number}
**One PTAB proceeding by trial number, with its decisions**

Answers: what happened in IPR2026-00047 — the proceeding's parties, dates and status, the folded `instituted` / `claims_outcome` / `final_decision_date`, and its `decisions` list (same shape as /v1/patents/{n}/ptab). Pass include_text=true for the full text of each decision.

Parameters:
- `trial_number` [path] (required): PTAB trial number: IPR2026-00047, PGR2026-00074, CBM..., DER... (case and spaces ignored).
- `include_text` [query]: Include the full text of each decision.

## GET /v1/ptab
**Search PTAB proceedings by petitioner, patent owner, filing date or status**

Answers: which patents is this petitioner attacking, which of this owner's patents are being challenged, or what was filed after a date. `petitioner` / `patent_owner` accept an entity id (ent_...), a family id (fam_...) or a case-insensitive name fragment. At least one filter is required. Paginated newest filed first. Costs 1 unit per 100 rows.

Parameters:
- `petitioner` [query]: Entity id or name fragment of the petitioner.
- `patent_owner` [query]: Entity id or name fragment of the patent owner.
- `filed_after` [query]: Only petitions filed strictly after this ISO date.
- `status` [query]: USPTO trialStatusCategory, e.g. 'Instituted', 'Terminated' (case-insensitive exact).
- `limit` [query]: Page size. Each 100 rows returned costs 1 request unit.
- `cursor` [query]: Opaque cursor from a previous response's next_cursor. Omit for the first page.

## GET /v1/patents/{patent_number}/maintenance
**Is this patent alive? Maintenance fees and expiration**

Answers: is this patent still in force, have its 3.5/7.5/11.5-year maintenance fees been paid, and when does it expire. Returns `events` (every fee event: paid, reminder, expired, reinstated), `windows` {"3.5y", "7.5y", "11.5y": paid|due|expired|n/a}, `expired` (true once a fee lapsed or the term ended), `expiration_reason`, and `expiration_estimate` (`term_start` + 20 years + PTA, where `term_start` is the earliest filing date the patent claims benefit from under 35 U.S.C. 120/121/365(c), else its own filing date; `estimate: true` because terminal disclaimers may shorten it). Window values: paid, due (payable now), grace (overdue, surcharge), expired, upcoming (not yet payable), n/a (design/plant/reissue owe no fees); `deadlines` carries each window's opens/due/surcharge_until dates and `next_fee` the first unpaid window (null when the patent is not alive).

Parameters:
- `patent_number` [path] (required): US patent number without commas; design/plant/reissue prefixes allowed (D912345, PP12345, RE48123).

## GET /v1/entities/search
**Resolve a company or inventor name to an entity id**

Answers: which entity is 'Impossible Foods' (or any assignee / inventor name) in the USPTO record, and what are its known name variants. Start here before /v1/entities/{id}/applications, /v1/assignments?assignee=, or /v1/ptab?patent_owner=. Results are ranked (prefix matches first) and include `aliases`, `location`, and `counts` {applications, patents, pending}. A company printed under several names is one row with a `fam_...` family id and `members`; a family id is accepted wherever an entity id is. `type` filters to assignee or inventor. Costs 1 unit per 100 rows.

Parameters:
- `q` [query] (required): Name to resolve; punctuation, case and corporate suffixes are ignored.
- `type` [query]: assignee or inventor.
- `limit` [query]: 

## GET /v1/entities/{entity_id}
**One entity with aliases and portfolio counts**

Answers: who is this entity id (or `fam_...` family id) — canonical name, name variants, location, `family_id`, and how many applications / patents / pending applications it has (a family's counts span its `members`).

Parameters:
- `entity_id` [path] (required): 

## GET /v1/entities/{entity_id}/applications
**An entity's applications (portfolio), filterable by status**

Answers: what has this company filed, and what is each filing's state right now. Each row carries the application's current `status` {code, description, category, bucket, date}, `patent_number` when granted, and the link `role`/`confidence`. `status` filters on USPTO's raw category: pending, granted, abandoned, allowed, expired, provisional, pct, other. `status.bucket` is what /v1/entities/{id}/stats counts (allowed folds into pending, expired-after-grant into granted, never-examined into other, null for an application we do not hold), so count buckets to reconcile a portfolio against the statistics. Newest serial first; paginate with `next_cursor`. Costs 1 unit per 100 rows.

Parameters:
- `entity_id` [path] (required): 
- `status` [query]: Status category filter: pending | granted | abandoned | allowed | expired | provisional | pct | other.
- `limit` [query]: Page size. Each 100 rows returned costs 1 request unit.
- `cursor` [query]: Opaque cursor from a previous response's next_cursor. Omit for the first page.

## GET /v1/entities/{entity_id}/stats
**What is this company doing: portfolio size, velocity, prosecution, technology, examiners, PTAB exposure, deals**

Answers: how big is this company's US patent position, is it growing, how well does it prosecute, what technology is it in, who examines it, is it under attack at the PTAB, and is it buying or selling patents. One object of blocks — `portfolio` {applications, patents, pending, granted, abandoned, alive_patents, expiring_within_12_months, computed_over}, `velocity` {filings_per_year, grants_per_year: [{year, n}]}, `prosecution` (the same metrics as /v1/examiners/{name}/stats over this entity's applications: allowance_rate, median_months_to_first_action, median_months_to_grant, avg_office_actions_per_disposed, rce_rate, appeal_rate), `where` {cpc_subclasses [{subclass, n, share}], cpc_coverage, art_units}, `who` {examiners [{name, key, n}], prosecuted_by {customer_numbers}}, `ptab` {as_patent_owner {proceedings, instituted, claims_lost}, as_petitioner}, `ownership` {acquired, sold, since} — every number defined in `definitions`. Portfolio membership is that of /v1/entities/{id}/applications (title per recorded assignments beats filing). Rates are null when undefined; read `cpc_coverage` before trusting the subclasses. Large entities are precomputed nightly, the rest on first request (`computed_at`); 504 computation_timeout means the entity is queued for the next nightly run. Costs 1 request.

Parameters:
- `entity_id` [path] (required): 

## GET /v1/entities/{entity_id}/patents
**An entity's granted patents**

Answers: which patents does this company hold (by applicant, assignee or inventor link), with grant date, expiration estimate and whether each has expired. Newest number first; paginate with `next_cursor`. Costs 1 unit per 100 rows.

Parameters:
- `entity_id` [path] (required): 
- `limit` [query]: Page size. Each 100 rows returned costs 1 request unit.
- `cursor` [query]: Opaque cursor from a previous response's next_cursor. Omit for the first page.

## GET /v1/examiners/{examiner_name}/stats
**How does this examiner prosecute: allowance rate, time to first action, RCE and appeal rates**

Answers: what are my odds and how long will it take with this examiner. Precomputed nightly over every application the examiner is listed on: `counts` by status (pending, granted, abandoned, other), `allowance_rate`, `median_months_to_first_action`, `median_months_to_disposal`, `avg_office_actions_per_disposed`, `rce_rate`, `appeal_rate`, `art_units`, plus `sample_size`, `computed_over` (filing-date range) and `definitions`. The name is matched case- and whitespace-insensitively ('smith, jane' = 'SMITH, JANE'). 404 not_found when there are no statistics for that name yet: the tables are rebuilt nightly, so a newly listed examiner appears the next day; USPTO's docket placeholders ('CENTRAL, DOCKET') never do.

Parameters:
- `examiner_name` [path] (required): Examiner name as USPTO prints it, 'LAST, FIRST M' (any case).

## GET /v1/art-units/{art_unit}/stats
**How does this art unit prosecute: allowance rate, time to first action, RCE and appeal rates**

Answers: what does prosecution look like in this USPTO art unit (groupArtUnitNumber, e.g. 2857). Same metrics as /v1/examiners/{name}/stats aggregated over every application in the art unit, plus `examiners` (distinct count). 404 not_found for an unknown art unit.

Parameters:
- `art_unit` [path] (required): USPTO art unit number, e.g. 2857.

## GET /v1/search
**Find patents and applications by keywords, CPC class, filing date and owner**

Answers: which granted patents / published applications are about X — by words in the title, abstract or claims (`q`, websearch syntax: words are ANDed, "quoted phrases" must be adjacent, `-word` excludes, `or` alternates; English stemming), and/or in a CPC level (`cpc`: C, C12, C12N, C12N15 or C12N15/88 — one level, not a free-form prefix), optionally only `doc_type` grant|publication, filed within `filed_after`..`filed_before` (inclusive, on the application's filing date), and/or in one entity's portfolio (`entity_id` from /v1/entities/search). At least one of q / cpc / entity_id is required (400 otherwise). This is keyword search over the weekly full-text products, not similarity or prior-art search. Each result: `doc_number`, `doc_type`, `kind`, `application_number`, `title`, `date` (grant/publication), `filing_date`, `cpc`, `snippet` (abstract with **matches** marked) and `rank` (ts_rank_cd; null without q). Ranked best first, then newest; page with `next_cursor`. `total` is the number of matches up to 10,000 (`total_capped: true` beyond). A query that runs longer than 10 s is a 504 computation_timeout: add a cpc level, doc_type, a date range or more specific words. Status of any hit: /v1/applications/{application_number}; its full claims: /v1/patents/{n}/claims. Costs 1 unit per 100 rows.

Parameters:
- `q` [query]: Websearch query over title, abstract and claims: mRNA "lipid nanoparticle" -virus
- `cpc` [query]: One CPC level: C, C12, C12N, C12N15 (or C12N15/), C12N15/88.
- `doc_type` [query]: grant or publication; omit for both.
- `filed_after` [query]: Only applications filed on/after this ISO date.
- `filed_before` [query]: Only applications filed on/before this ISO date.
- `entity_id` [query]: Entity id (ent_...) or family id (fam_...) from /v1/entities/search: only that portfolio.
- `limit` [query]: Page size. Each 100 rows returned costs 1 request unit.
- `cursor` [query]: Opaque cursor from a previous response's next_cursor. Omit for the first page.

## POST /agent/signup
**Agent Signup**

## POST /agent/checkout-link
**Agent Checkout Link**

## POST /webhooks/stripe
**Stripe Webhook**

## POST /keys
**Mint Key**

## GET /agent/me
**Agent Me**

## GET /v1/monitors
**List your monitors**

## POST /v1/monitors
**Watch applications, patents, or entities and get webhooks**

Creates a monitor. When our nightly ingest detects one of `events` on any target, we POST the event to `webhook_url` signed with HMAC-SHA256 of `secret` in `X-Signature`. Each delivery costs 1 request. The response carries `secret` exactly once (generated when you did not pass one); store it, and `coverage_since` — the monitor fires on what we detect from now on, and nothing before that time was ever seen.

## DELETE /v1/monitors/{monitor_id}
**Stop a monitor**

Parameters:
- `monitor_id` [path] (required): 

## GET /v1/monitors/{monitor_id}/events
**Events matched by a monitor and their delivery status**

Every event this monitor matched, newest first, each with its delivery attempts and last status. `coverage_since` is when event detection began: nothing before it is here, whatever happened. Returns {monitor_id, count, coverage_since, events: [{event_id, event_type, occurred_on, detected_at, application_number, patent_number, entity_id, detail, attempts, last_status, delivered_at}]}.

Parameters:
- `monitor_id` [path] (required): 
- `since` [query]: ISO date; only events detected on/after this date
- `limit` [query]: 
