# PTAB Trials Scraper — USPTO IPR/PGR Tracker + Webhook Alerts (`nomad-agent/ptab-trials-scraper`) Actor

Track USPTO PTAB patent litigation via the official Open Data Portal API: IPR, PGR, CBM & DER proceedings with normalized parties, status & key dates, plus institution & final-written decisions. Webhook + delta alerts push new filings and rulings to Slack/n8n. Free USPTO key (BYOK), $0.004/record.

- **URL**: https://apify.com/nomad-agent/ptab-trials-scraper.md
- **Developed by:** [Nomad.Dev](https://apify.com/nomad-agent) (community)
- **Categories:** AI, Developer tools, Automation
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 ptab record results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## PTAB Trials Scraper — USPTO IPR/PGR Tracker + Webhook Alerts

> **Claude / Codex skill to describe and setup this actor: [SKILL.md](https://github.com/Exdenta/OinkAIJobSearch/blob/main/skill/ptab-trials-scraper/SKILL.md)**

Track **Patent Trial and Appeal Board (PTAB)** AIA trials through USPTO's official **Open Data Portal (ODP) API** — inter partes review (IPR), post-grant review (PGR), covered business method (CBM) and derivation (DER) proceedings, with normalized parties, patent, status and key dates. Or fetch each trial's **decision documents** (institution decisions, final written decisions) joined with their proceeding context.

This is the **patent-litigation data** layer behind commercial PTAB analytics products — institution monitoring, petitioner/patent-owner tracking, final-written-decision alerting — as clean flat **IP data** (JSON) you can query, export or pipe into your own analysis. Built for patent litigators, litigation-finance underwriters, NPE/licensing teams and competitive-intel analysts. Bring your own **free** USPTO API key.

| | This Actor | Seat-priced PTAB analytics (Docket Navigator, Unified Patents…) |
|---|---|---|
| Pricing | pay per record, no contract | subscription, sales-gated |
| New-filing / new-ruling alerts | **webhook push** + delta mode | included, per seat |
| Data source | official USPTO ODP API | proprietary pipelines |
| Output | flat JSON / CSV / Excel you own | locked in a web UI |

- **Official API only** — api.uspto.gov, the ODP successor of USPTO's retired `developer.uspto.gov` PTAB API. This Actor deliberately does **not** scrape P-TACTS (ptacts.uspto.gov), whose FAQ prohibits bulk automated access.
- **Rate-limit compliant** — USPTO's published limits (burst 1, no parallel calls per key) are enforced; all requests are serialized.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `usptoApiKey` **(required, unless `dryRun`)** | string (secret) | — | Your free USPTO ODP API key, sent as `X-API-KEY` only to api.uspto.gov. [How to get one](#how-do-i-get-a-uspto-api-key) takes a few minutes. |
| `dryRun` | boolean | `false` | Preview the output shape with **no key, no USPTO call and no billing** — returns a fixed sample record for the selected `mode`. See [Dry run](#dry-run-preview-without-a-key). |
| `mode` | string | `"proceedings"` | `"proceedings"` = one record per trial. `"decisions"` = one record per decision document, enriched with proceeding context (one extra API call per matched trial — slower on broad searches). |
| `trialNumber` | string | *(empty)* | Look up one trial, e.g. `"IPR2024-00123"`. When set, every other filter is ignored. |
| `query` | string | *(empty)* | Free-text query passed to the USPTO search API. Ignored when `trialNumber` is set. |
| `proceedingTypes` | array (multi-select) | *(empty = all)* | Any of `IPR`, `PGR`, `CBM`, `DER`. |
| `statusCategory` | string | *(empty)* | Exact USPTO status category, e.g. `"Institution Granted"`, `"Institution Denied"`, `"Final Written Decision"`. Free text — USPTO publishes no closed list; run once unfiltered and read `statusCategory` values back. |
| `patentNumber` | string | *(empty)* | Only trials challenging this US patent (digits only, e.g. `"10701173"`). |
| `patentOwnerName` | string | *(empty)* | Patent owner as recorded by USPTO (exact-name matching — prefer `query` when unsure). |
| `petitionerName` | string | *(empty)* | Petitioner real party in interest as recorded by USPTO (same exact-name caveat). |
| `technologyCenter` | string | *(empty)* | Only trials whose challenged patent sits in this USPTO technology center, e.g. `"2600"`. |
| `artUnit` | string | *(empty)* | Only trials whose challenged patent sits in this USPTO group art unit, e.g. `"2645"`. |
| `filedFrom` / `filedTo` | date | *(empty)* | Accorded-filing-date range (both modes filter on the trial's filing date). |
| `onlyNewSinceLastRun` | boolean | `false` | Only push records not seen in previous runs — see [Delta mode](#delta-mode-monitoring). Skipped records are not billed. |
| `webhookUrl` | string | *(empty)* | Optional HTTP(S) endpoint the run also POSTs records to — see [Webhook alerts](#webhook-alerts). Best-effort; a webhook failure never fails the run. |
| `webhookSecret` | string (secret) | — | Optional shared secret sent as the `X-Webhook-Secret` header so your endpoint can verify the caller. |
| `maxItems` | integer | `100` | Hard cap on records pushed per run (1–10000). |

### What PTAB data does this scraper extract?

One flat JSON record per **proceeding** (`"recordType": "proceeding"`):

| Field | Meaning |
|---|---|
| `id` | Stable id — the trial number |
| `recordType` | `"proceeding"` |
| `source` | `"uspto-odp-ptab"` |
| `trialNumber` | e.g. `IPR2024-00123` |
| `proceedingType` | `IPR` / `PGR` / `CBM` / `DER` |
| `statusCategory` | USPTO trial status category |
| `patentNumber` | Challenged US patent |
| `patentOwner` | Patent owner name |
| `petitioner` | Petitioner real party in interest |
| `petitionerCounsel` / `patentOwnerCounsel` | Counsel names where recorded |
| `inventorName` | Inventor(s) where recorded |
| `applicationNumber` | Underlying application number |
| `grantDate` | Patent grant date (`YYYY-MM-DD`) |
| `technologyCenter` / `artUnit` | USPTO tech center / group art unit |
| `petitionFilingDate` / `accordedFilingDate` | Petition / accorded filing dates |
| `institutionDecisionDate` / `institutionDate` | Institution decision / institution dates |
| `finalDecisionDate` | Final decision (termination) date |
| `lastModified` | USPTO last-modified stamp |
| `apiUrl` | Direct ODP API URL for this trial |
| `parseConfidence` | `0.0–1.0` — drops when core fields are missing upstream |
| `warnings` | List of normalization warnings (empty when clean) |
| `isNew` | `true` on every pushed record — only present in [delta mode](#delta-mode-monitoring) |

One flat JSON record per **decision document** (`"recordType": "decision"`): `id` (`trialNumber:documentIdentifier`), `recordType`, `source`, `trialNumber`, `proceedingType`, `decisionType`, `decisionOutcome`, `claimsChallenged`, `claimsFoundUnpatentable`, `decisionDate`, `documentIdentifier`, `documentName`, `documentUrl` (PDF download URI), plus proceeding context copied in (`statusCategory`, `patentNumber`, `patentOwner`, `petitioner`, `accordedFilingDate`), `apiUrl`, `parseConfidence`, `warnings`. Decision-detail fields (`decisionType`, `decisionOutcome`, claims fields) are extracted defensively from the newer ODP decisions payload and are `null` with a warning when USPTO omits them — never fabricated.

### How to track PTAB trials with this Actor

1. Paste your **USPTO API key** (free — see [FAQ](#how-do-i-get-a-uspto-api-key)).
2. Pick **what to fetch**: trial-level `proceedings` or document-level `decisions`.
3. Filter by **proceeding type**, **patent number**, **patent owner**, **petitioner**, **status** or **filing-date range** — or set **trialNumber** for a single docket.
4. Turn on **Only new since last run** and schedule it to watch for new petitions against a patent portfolio or fresh final written decisions.
5. Run and export JSON, CSV or Excel — or call it over the API:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("nomad-agent/ptab-trials-scraper").call(run_input={
    "usptoApiKey": "<YOUR_USPTO_API_KEY>",
    "mode": "proceedings",
    "proceedingTypes": ["IPR"],
    "petitionerName": "Apple Inc.",
    "filedFrom": "2025-01-01",
    "maxItems": 100,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["trialNumber"], "|", item["statusCategory"], "|",
          item["patentOwner"])
```

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/nomad-agent~ptab-trials-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"usptoApiKey": "<YOUR_USPTO_API_KEY>", "mode": "proceedings", "patentNumber": "10701173", "maxItems": 50}'
```

### Output example

Illustrative proceeding record (field structure is exact; values shown are examples):

```json
{
  "id": "IPR2025-00123",
  "recordType": "proceeding",
  "source": "uspto-odp-ptab",
  "trialNumber": "IPR2025-00123",
  "proceedingType": "IPR",
  "statusCategory": "Institution Granted",
  "patentNumber": "10701173",
  "patentOwner": "Example Patent Holdings LLC",
  "petitioner": "Example Technology Inc.",
  "petitionerCounsel": "J. Smith",
  "patentOwnerCounsel": "A. Jones",
  "inventorName": "R. Doe",
  "applicationNumber": "15123456",
  "grantDate": "2020-06-30",
  "technologyCenter": "2600",
  "artUnit": "2645",
  "petitionFilingDate": "2025-01-14",
  "accordedFilingDate": "2025-01-15",
  "institutionDecisionDate": "2025-07-20",
  "institutionDate": "2025-07-20",
  "finalDecisionDate": null,
  "lastModified": "2026-06-30T05:12:44",
  "apiUrl": "https://api.uspto.gov/api/v1/patent/trials/proceedings/IPR2025-00123",
  "parseConfidence": 1.0,
  "warnings": []
}
```

### Delta mode (monitoring)

Turn on **Only new since last run** (`onlyNewSinceLastRun`) and the Actor remembers every record id it has already returned (proceeding: trial number; decision: `trialNumber:documentIdentifier`) in a named key-value store that persists across runs.

- Already-seen records are **not pushed and not billed**.
- Every pushed record is tagged `"isNew": true`.
- Ids are committed to the seen-store **after** a successful dataset push, so a transient push failure never silently marks a record seen without delivering it.
- Schedule the same search daily to get **only** newly filed petitions, newly instituted trials or newly entered decisions — the exact workflow PTAB analytics subscriptions charge for.

### Webhook alerts

Set **`webhookUrl`** (any HTTP/HTTPS endpoint — Slack workflow, n8n, Make, Zapier catch-hook, your own API) and, after each run writes records to the dataset, the Actor also **POSTs those records** to your endpoint as JSON, in batches of 50:

```json
{ "source": "uspto-odp-ptab", "items": [ /* PTAB records */ ], "offset": 0, "count": 50, "total": 128 }
```

- Pair it with **Only new since last run** + a schedule and you get push alerts on **new IPR/PGR petitions, institution decisions and final written decisions** the moment they enter — no dataset polling.
- Set **`webhookSecret`** and it is sent as the `X-Webhook-Secret` header so your endpoint can verify the caller (the secret is never logged).
- Delivery is **best-effort and fail-open**: a webhook error is logged and the run still finishes `SUCCEEDED` with all records in the dataset.

### Dry run (preview without a key)

Turn on **`dryRun`** to see the exact output shape before you spend anything: the Actor returns a fixed sample record for the selected `mode`, makes **no USPTO API call**, needs **no API key**, and **bills nothing** (no start fee, no per-record charge). Turn it off and add your `usptoApiKey` for live data.

### Related Actors

Part of a small family of structured government / legal-data Actors — same clean-JSON, official-source, delta-mode approach:

- [UK Case Law Scraper](https://apify.com/nomad-agent/uk-case-law-scraper) — UK court judgments from the official Find Case Law API.
- [SPAC Redemptions Scraper](https://apify.com/nomad-agent/spac-redemptions-scraper) — SPAC redemption & lifecycle events from SEC EDGAR.

### Integrations

Export results as JSON, CSV or Excel, or wire this Actor into [Make](https://make.com), [Zapier](https://zapier.com) or [n8n](https://n8n.io); call it programmatically with `run-sync-get-dataset-items`; or use it from AI agents via the [Apify MCP server](https://mcp.apify.com).

### Pricing

Pay per event: **$0.004 per record returned** plus a small per-run start fee ($0.005 from July 20, 2026; $0.01 before).
100 proceedings ≈ $0.41. No subscription — pay only for what you fetch. Your USPTO API key itself is free (USPTO does not charge for ODP access).

### Use cases

- Monitor new IPR/PGR petitions against your (or a competitor's) patent portfolio
- Alert on institution decisions and final written decisions as they enter
- Build institution-rate and outcome datasets by petitioner, owner, tech center or art unit
- Feed litigation-finance, licensing and prior-art workflows with structured PTAB dockets
- Self-serve alternative to seat-priced PTAB analytics subscriptions

### FAQ

**Is it legal to scrape this data?**
Yes. This Actor calls USPTO's official, documented Open Data Portal API with your own key. PTAB records are US-government works in the public domain. It never touches the P-TACTS web UI, whose FAQ discourages bulk automated access — the ODP API is USPTO's designated machine-access route, including bulk use.

**How do I get a USPTO API key?**
Free, a few minutes: create a USPTO.gov account at https://account.uspto.gov/profile/create-account, then open **MyODP** at https://data.uspto.gov/myodp (first login walks you through identity verification via ID.me) and copy your API key. Paste it into `usptoApiKey` — Apify stores it encrypted.

**Why do I get "Unauthorized"/"Forbidden"?**
`401` = key missing, `403` = key invalid/not yet active. Check the key in your MyODP page; new keys can take a few minutes to activate.

**How fresh is the data?**
Every run hits the live ODP API. USPTO ingests PTAB filings continuously; `lastModified` on each record carries USPTO's own stamp.

**Does it cover ex parte appeals or interferences?**
Not yet — this Actor covers AIA trials (IPR/PGR/CBM/DER). The ODP appeals and interferences endpoints are separate; open an issue if you need them.

**Why are some decision fields null?**
USPTO's decisions payload does not carry every analytic field for every document. Missing values are `null` and listed in `warnings` — never guessed. `parseConfidence` summarizes record completeness.

**Something broken or missing?**
Open an issue on the Actor's **Issues** tab — it is monitored and fixes ship fast.

# Actor input Schema

## `usptoApiKey` (type: `string`):

Your free USPTO Open Data Portal API key (sent as X-API-KEY). Get one in minutes: create a USPTO.gov account, then open https://data.uspto.gov/myodp and copy your key. The key stays on Apify as an encrypted secret; this Actor sends it only to api.uspto.gov. Not needed when Dry run is on.

## `dryRun` (type: `boolean`):

Preview the exact output shape without a USPTO key: returns a fixed sample record for the selected mode, makes no USPTO API call and bills nothing (no start or per-record charge). Turn off and add your usptoApiKey to fetch live PTAB data.

## `mode` (type: `string`):

<code>proceedings</code> — one record per PTAB trial (parties, patent, status, key dates). <code>decisions</code> — one record per decision document (institution decisions, final written decisions...), each joined with its proceeding's parties/patent/status context. Decisions mode makes one extra API call per matched trial, so it is slower on broad searches.

## `trialNumber` (type: `string`):

Look up one specific trial, e.g. <code>IPR2024-00123</code>. When set, all other filters below are ignored: you get that trial's proceeding record (proceedings mode) or all of its decision documents (decisions mode).

## `query` (type: `string`):

Optional free-text query passed to the USPTO search API (parties, patent numbers, technology keywords). Leave empty to browse by the structured filters below only. Ignored when Trial number is set.

## `proceedingTypes` (type: `array`):

Restrict to specific AIA trial types. Empty = all types. Ignored when Trial number is set.

## `statusCategory` (type: `string`):

Optional exact status-category filter as used by the USPTO API, e.g. <code>Institution Granted</code>, <code>Institution Denied</code>, <code>Final Written Decision</code>. Free text because USPTO does not publish a closed list of values; run without this filter once and read the statusCategory field to see live values. Ignored when Trial number is set.

## `patentNumber` (type: `string`):

Only trials challenging this US patent, e.g. <code>10701173</code> (digits only, no commas). Ignored when Trial number is set.

## `patentOwnerName` (type: `string`):

Only trials where the patent owner matches this name as recorded by USPTO (e.g. <code>Samsung Electronics Co., Ltd.</code>). The API matches the recorded name — try the free-text Search query instead if you are unsure of the exact form. Ignored when Trial number is set.

## `petitionerName` (type: `string`):

Only trials where the petitioner's real party in interest matches this name as recorded by USPTO (e.g. <code>Apple Inc.</code>). Same exact-name caveat as Patent owner. Ignored when Trial number is set.

## `technologyCenter` (type: `string`):

Only trials whose challenged patent sits in this USPTO technology center, e.g. <code>2600</code> (Communications) or <code>1600</code> (Biotech/Organic). Exact match on the value USPTO recorded. Ignored when Trial number is set.

## `artUnit` (type: `string`):

Only trials whose challenged patent sits in this USPTO group art unit, e.g. <code>2645</code>. More specific than technology center; exact match on the recorded value. Ignored when Trial number is set.

## `filedFrom` (type: `string`):

Only trials with an accorded filing date on/after this date. Ignored when Trial number is set.

## `filedTo` (type: `string`):

Only trials with an accorded filing date on/before this date. Ignored when Trial number is set.

## `onlyNewSinceLastRun` (type: `boolean`):

Only output proceedings/decisions not seen in previous runs with this flag. Skipped (already-seen) records are not pushed or billed — schedule the same search to watch for new IPR petitions against your patents or fresh final written decisions.

## `webhookUrl` (type: `string`):

Optional HTTP(S) endpoint. When set, after writing records to the dataset the run also POSTs them to this URL as JSON (<code>{source, items, offset, count, total}</code>) in batches of 50. Pair with Only-new + a schedule to push new institution/final-written decisions straight to Slack, n8n, Make or email. Best-effort: a webhook failure is logged but never fails the run.

## `webhookSecret` (type: `string`):

Optional shared secret sent with each webhook POST as the X-Webhook-Secret header so your endpoint can verify the caller. Only used when Webhook URL is set; never logged.

## `maxItems` (type: `integer`):

Hard cap on records pushed per run (1–10000). In decisions mode this caps decision records; one trial can yield several decision documents.

## Actor input object example

```json
{
  "dryRun": false,
  "mode": "proceedings",
  "onlyNewSinceLastRun": false,
  "maxItems": 100
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("nomad-agent/ptab-trials-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("nomad-agent/ptab-trials-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call nomad-agent/ptab-trials-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=nomad-agent/ptab-trials-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/muANU3J0qDyFF4SHH/builds/1ELRGPKn6GXKh0BHV/openapi.json
