Patent Citation Overlap Analyzer avatar

Patent Citation Overlap Analyzer

Pricing

from $28.00 / 1,000 citation assessments

Go to Apify Store
Patent Citation Overlap Analyzer

Patent Citation Overlap Analyzer

Compare observed patent citation sets across a supplied dataset. Report shared references, directional overlap measures and citation coverage limits for each patent assessment.

Pricing

from $28.00 / 1,000 citation assessments

Rating

0.0

(0)

Developer

Tim Zinin

Tim Zinin

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

Turn a batch of Google-Patents-style records into a per-patent citation overlap assessment: which patents in your own batch share backward or forward references, by exact count, with honest gaps disclosed.

Patent Citation Overlap Analyzer buyer input, source evidence and decision outcome

What you get

For every distinct, usable patentId in the batch you supply, this Actor delivers one paid citation assessment row that compares that patent's observed citation sets against every other patent present in the same batch. It is not a prior-art search, not a novelty or validity opinion, and not a claim about a patent's complete real-world citation graph -- it is an exact, evidence-linked comparison of what your supplied rows actually contained, scoped to the batch you sent.

Each assessment separates the two citation directions and never blends them:

  • Backward citations -- the references this patent's own filing makes to earlier patents (citationsBackward).
  • Forward citations -- the later patents that cite this one (citationsForward).

For every direction, the Actor reports the distinct count observed, the source's own reported total (when supplied), and whether that total discloses a partial array (source said there are more citations than the array actually contains). For every other patent in the same batch sharing at least one citation ID in either direction, it reports the exact intersection and union size per direction -- never a single blended similarity score -- ranked by combined overlap strength.

The Actor also surfaces three classes of data-quality signal that a naive citation comparison silently drops:

  1. Missing vs. zero. A field never a usable array on any accepted row is null (unknown), never coerced to 0. An array the source reported as empty ([]) is observed 0. "We don't know" and "we checked and there are none" are never conflated.
  2. Record conflicts. When duplicate rows for the same patentId disagree -- different citation sets or totals -- the assessment records the disagreement explicitly (recordConflicts) instead of silently picking one; the row is marked partial.
  3. Self-citations. Real upstream data sometimes has a patent citing itself. The Actor reports this as-is (selfCitationInBackward/selfCitationInForward), never silently removed or treated as an error.

Every useful assessment -- including a genuine, honestly observed zero-overlap result -- is delivered with evidence: a bounded, ranked list of the citation coverage this specific patent had and the peers it actually shares references with, each item traceable back to a specific sourceRowIndex in the batch you sent. A zero-overlap patent is not a failed lookup; it is a paid, useful fact about your supplied batch (see Pricing below for exactly when a result is and is not billed).

Source snapshot moving through batch comparison into a review-routing decision

Who uses it

  • Patent portfolio and IP operations teams who already run a Google-Patents-style scraper (for example khadinakbar/google-patents-scraper) against a shortlist of their own or a competitor's patents, and want to see, without opening each patent page by hand, which of those specific patents share references in the observed citation sets before deciding which pairs deserve a manual landscape review.
  • Technology scouting and licensing analysts comparing a candidate acquisition or licensing target's patent family against internal or competitor patents already scraped into the same batch, to spot citation clusters worth a closer look.
  • Patent prosecution paralegals and docketing teams who need a fast, mechanical first pass over a batch of related filings to flag overlapping citation activity before a human reviewer spends time on full-text comparison.
  • Data and analytics teams building patent-landscape dashboards who want a bounded, evidence-linked overlap signal they can pipe into a CRM or internal tool via dataset export, webhook, or the API, instead of hand-rolling set-intersection code.
  • Anyone auditing a citation scraper's own data quality -- every count, conflict and self-citation flag traces directly to what the source rows contained, so this Actor doubles as an honest diagnostic on the upstream scrape (missing arrays and disagreeing duplicates surface as partial and recordConflicts).

This Actor does not serve prior-art search, patentability opinions, infringement risk scoring, or any legal validity conclusion. See "Evidence and boundaries" for the explicit list of claims it never makes.

How to run

1. Get patent rows into a Dataset, or paste them inline

This Actor never scrapes patents itself and never starts another Actor. It reads rows that already exist -- either pasted inline, or read from an existing Apify Dataset or run. The rows it expects are Google-Patents-shaped: each row needs at minimum a patentId and, for a useful (non-diagnostic) assessment, at least one of citationsBackward / citationsForward as an actual array.

The fastest path is to run a Google-Patents-style scraper Actor such as khadinakbar/google-patents-scraper (mode: "details") against the patents you care about, then either point Source Dataset at that run's Dataset (resource-picker, READ permission), pass Source run ID and let this Actor resolve the Dataset itself, or paste rows into Source records (inline).

2. Choose maxResults and maxInputRows

  • maxInputRows (default 100, max 1000) bounds how many source rows are read -- a coverage limit on the input side, reported honestly via sourceCoverage / sourcePartial if the batch is larger than the cap.
  • maxResults (default 2, max 1000) bounds how many paid citation assessment rows this run will deliver. It has nothing to do with how many patents are compared internally -- pair comparisons run over every usable patent in the read batch (bounded to the first 1000 distinct, alpha-sorted patentIds); maxResults only caps how many of the resulting assessment rows are actually written and charged. Free diagnostics (invalid rows, missing IDs, budget/cap notices) are not counted against maxResults and are never charged.

3. Run it and read the Dataset

Each run writes to the default Dataset: one billable row per useful citation assessment (found: true), plus free diagnostic rows for anything that could not become useful (missing patentId, source row failure, batch-size exclusion, budget/cap stops). The key-value store's OUTPUT/SUMMARY record -- see "Output examples" and "Operating guide" -- gives the run-level tally: rows requested, useful, delivered, paid, free, failed, and whether maxResults, Max total charge, or the source itself capped delivery.

Pricing

The single billing noun is citation assessment: one delivered row (found: true) for one usable, distinct patentId in your batch. There is no start fee and no per-peer or per-pair charge -- a patent that overlaps with nine other patents in your batch is still exactly one assessment, not nine.

TierPrice per citation assessment
Free (list price)$0.04
Bronze$0.036 (10% off)
Silver$0.032 (20% off)
Gold$0.028 (30% off)
Platinum$0.028 (30% off)
Diamond$0.028 (30% off)

These are the Actor's six live pricing tiers as configured on Apify's pay-per-event model (result-found is the only named billable event; the runtime requires a zero default Dataset-item price before delivery). Apify applies your account's own volume tier automatically; there is nothing to configure on the Actor side.

What is and is not billed:

  • A patent with zero overlapping peers in the batch is still a real, useful, billed assessment (action: "no_overlap_in_batch") -- the Actor observed the patent's full available citation coverage and honestly found no shared citation IDs with anything else you sent it. "We checked and found no overlap in this batch" is a fact, not an empty result.
  • A patent whose accepted rows carried neither a usable citationsBackward nor a usable citationsForward array produces a free diagnostic (missing_citation_data), never a paid row -- there is nothing to compare, so nothing useful was produced.
  • A partial result (one direction unknown, a coverage-partial total, a record conflict, or capped peer/conflict evidence) is still billed if it is otherwise useful (found: true) -- partial does not mean free. It means the delivered fact is honestly qualified, not that no fact was delivered.
  • Rows dropped for exceeding the 64 KiB per-result size cap, or blocked because your run's Max total charge cannot cover another assessment, are never billed -- see "Operating guide" for how those show up in the Dataset.
  • This is bounded, non-network, synchronous JavaScript processing over the rows you supply -- full compute cost is real (it is what the price above is set against), but there is no per-row external API or model call. The price is not a markup on an upstream metered API; it is set against Apify's own compute unit cost for this bounded comparison work, independently for this Actor.

Input contract

The source resolver reconciles references before reading any records:

  1. It collects datasetId and payload.resource.defaultDatasetId. When both exist, they must identify the same Dataset. A Dataset reference, including one from the payload, takes precedence over inline rows.
  2. It collects runId and payload.resource.id; conflicting run IDs are rejected. With a Dataset and run reference together, it checks their correspondence when run metadata is readable. A verified mismatch is rejected; an unverifiable association remains explicitly unverified. With only a run ID, readable run metadata is required to resolve its default Dataset.
  3. With no Dataset or run reference, it reads inline items. Records from different source modes are never merged. A malformed supplied reference does not fall back to the saved inline example.

The Source Dataset and Source run ID fields use resource-picker permissions. The verified private integration test supplied an explicit datasetId with READ permission together with a matching payload.resource envelope. A raw webhook payload supplies identifiers, not permission to a private Dataset. Configure the mapping and access deliberately; no integration payload is assumed to appear automatically.

An explicitly supplied but malformed reference (invalid datasetId shape, a non-object payload.resource, disagreeing actorId claims) is rejected outright as invalid_input -- never silently treated as absent and falling through to inline rows, which would let a broken integration bill against demo data instead of the real request.

Bounds, identical for every source: at most 1000 rows total, 4 MiB combined, 64 KiB per individual row. A row larger than 64 KiB is dropped (and counted) rather than truncated or crashing the run. Reading a Dataset that keeps returning full pages up to your maxInputRows cap triggers one extra bounded probe read to check honestly whether more rows exist past the cap -- "unknown" is treated the same as "yes, more exists" for the purpose of the sourcePartial flag, never silently assumed complete.

Recognized row fields: patentId (identity), citationsBackward / citationsForward (citation ID arrays), citationsBackwardTotal / citationsForwardTotal (source-reported totals), scrapedAt / checkedAt (capture timestamp). Bibliographic fields such as title, assignee, cpc, filingDate and grantDate do not alter overlap counts. Source control fields are different: found:false, failed:true, a recognized failure status, or a nonempty error/errors reject a row. A source partial:true remains a partiality signal.

Output examples

The output field values below are copied from saved owner canaries on real khadinakbar/google-patents-scraper output -- not a synthetic fixture (run xeS0PgOkkhaNYLeF7, snapshot captured 2026-09-23). Field meanings are in "Field dictionary"; interpretation is in "Evidence and boundaries."

Scenario 1 -- default prefill (two patents, full citation coverage on both sides)

The Actor's own default example: two real, distinct patents (US10000000B2, a Raytheon LADAR patent; US10000001B2, an LS Mtron injection-molding patent) with full backward/forward arrays and matching totals. They share no citation IDs -- a real, honestly observed zero-overlap outcome, not a placeholder. The saved default reproduces these business metrics; a new run has its own processing timestamp.

Input (items, exactly as prefilled in the Actor's input form):

{
"items": [
{
"patentId": "US10000000B2",
"googlePatentsUrl": "https://patents.google.com/patent/US10000000B2/en",
"scrapedAt": "2026-09-23T06:42:42.482Z",
"mode": "details",
"title": "Coherent LADAR using intra-pixel quadrature detection",
"filingDate": "2015-03-10",
"publicationDate": "2018-06-19",
"grantDate": "2018-06-19",
"priorityDate": "2015-03-10",
"kindCode": "B2",
"countryCode": "US",
"type": "PATENT",
"status": "GRANT",
"assignee": "Raytheon Co",
"cpc": [
"G",
"G01",
"G01S",
"G01S7/00",
"G01S7/48",
"G01S7/483",
"G01S7/486",
"G01S7/4861",
"G01S7/4863",
"G01S13/00",
"G01S13/88",
"G01S13/89",
"G01S7/4865",
"G01S7/491",
"G01S7/4912",
"G01S7/4913",
"G01S7/4914",
"G01S7/4917",
"G01S17/00",
"G01S17/88",
"G01S17/89",
"G01S17/894"
],
"citationsBackward": [
"US5093563A",
"US5751830A",
"US20030076485A1",
"WO2005080928A1",
"US20060227317A1",
"JPS639877A",
"US5565982A",
"JP3332802B2",
"JP3771777B2",
"US6934034B2",
"JP2005214927A",
"US20060227316A1",
"US7206062B2",
"CN100578261C",
"JP5514641B2",
"US20130104661A1",
"JP2013257302A",
"JPWO2014203654A1",
"US10000000B2"
],
"citationsForward": [
"US10845468B2",
"US20180172806A1",
"US11237252B2",
"US20180156897A1",
"US20220326355A1",
"US12399278B1",
"US12399279B1",
"US12123950B2",
"US12418719B2",
"US10873738B2",
"US12096121B2",
"US11477363B2",
"US11838626B2",
"US11092690B1",
"US11619718B2",
"US11243296B2",
"US10753736B2",
"US11520021B2",
"US11933967B2",
"US12372318B2",
"US12379185B2",
"US12379177B2",
"US10031215B1",
"KR102373926B1",
"EP3548840B1",
"KR102254466B1",
"US11624828B2",
"KR102399757B1",
"US10502618B2",
"US9871339B1",
"US10002986B1",
"US10422880B2",
"DE112018001060T5",
"CN110832350B",
"EP4246176A3",
"CN113260873A",
"US11822010B2",
"US11415697B2",
"CN110456366B",
"IL321588A",
"US11548433B2",
"FR3112397B1",
"CN111708024B",
"EP4033270A4",
"KR102628708B1",
"US11740336B2",
"US12216209B2",
"US12153139B2",
"US11567212B2",
"CN113406664A",
"WO2023191849A2",
"US12130363B2",
"US12366663B1",
"US11592540B1",
"US12571914B2"
],
"citationsBackwardTotal": 19,
"citationsForwardTotal": 55,
"__source": {
"actor": "khadinakbar/google-patents-scraper",
"actorId": "9TNaptxQeIHf8bZNd",
"runId": "xiAfEHKEl32fXSeJH",
"datasetId": "DlbZZJT6kkyo0Utmq",
"sampleRowIndex": 0,
"projection": "Public business-record fields only; observed values unchanged. Added __source is provenance, not upstream payload."
}
},
{
"patentId": "US10000001B2",
"googlePatentsUrl": "https://patents.google.com/patent/US10000001B2/en",
"scrapedAt": "2026-09-23T06:42:46.090Z",
"mode": "details",
"title": "Injection molding machine and mold thickness control method",
"filingDate": "2015-12-08",
"publicationDate": "2018-06-19",
"grantDate": "2018-06-19",
"priorityDate": "2014-12-24",
"kindCode": "B2",
"countryCode": "US",
"type": "PATENT",
"status": "GRANT",
"assignee": "LS Mtron Ltd",
"cpc": [
"B",
"B29",
"B29C",
"B29C45/00",
"B29C45/17",
"B29C45/76",
"B29C45/64",
"B29C45/66",
"B29C45/80",
"B29C45/661",
"G",
"G05",
"G05B",
"G05B19/00",
"G05B19/02",
"G05B19/18",
"G05B19/182",
"G05B19/402",
"B29C2945/00",
"B29C2945/76",
"B29C2945/76003",
"B29C2945/76083",
"B29C2945/76177",
"B29C2945/76224",
"B29C2945/76227",
"B29C2945/76344",
"B29C2945/76431",
"B29C2945/76494",
"B29C2945/76505",
"B29C2945/76822",
"B29C2945/76866",
"B29C45/1751",
"B29C45/7653",
"G05B2219/00",
"G05B2219/30",
"G05B2219/45",
"G05B2219/45244"
],
"citationsBackward": [
"JP3276727B2",
"US20020022069A1",
"US20030039719A1",
"KR100512595B1",
"US20050098912A1",
"US20080211126A1",
"US20090026644A1",
"US7804268B2",
"JP2009208412A",
"US20120146260A1",
"US9296144B2",
"JP2013132796A",
"US20140134284A1",
"US9022767B2",
"US9090016B2",
"US9108352B2",
"US9566732B2",
"CN1428232A",
"JP3962725B2",
"JP4146381B2"
],
"citationsForward": [
"US12600071B2",
"CN109414860B",
"JP6444955B2",
"JP6538763B2",
"US11850782B2",
"CN111703053B"
],
"citationsBackwardTotal": 20,
"citationsForwardTotal": 6,
"__source": {
"actor": "khadinakbar/google-patents-scraper",
"actorId": "9TNaptxQeIHf8bZNd",
"runId": "xiAfEHKEl32fXSeJH",
"datasetId": "DlbZZJT6kkyo0Utmq",
"sampleRowIndex": 1,
"projection": "Public business-record fields only; observed values unchanged. Added __source is provenance, not upstream payload."
}
}
]
}

Delivered Dataset rows (both found: true, both paid -- 2 of 2 result-found events on this run):

[
{
"entityId": "patent:US10000000B2",
"found": true,
"status": "citation_overlap_assessed",
"action": "no_overlap_in_batch",
"confidence": "high",
"sourceRowCount": 1,
"patentId": "US10000000B2",
"backwardCitationCount": 19,
"forwardCitationCount": 55,
"backwardSourceReportedTotal": 19,
"forwardSourceReportedTotal": 55,
"backwardCoveragePartial": false,
"forwardCoveragePartial": false,
"selfCitationInBackward": true,
"selfCitationInForward": false,
"topOverlappingPeers": [],
"overlappingPeerCount": 0,
"overlappingPeerCountIsLowerBound": false,
"peerEvidenceOmitted": 0,
"recordConflicts": [],
"recordConflictCount": 0,
"recordConflictCountIsLowerBound": false,
"observedAt": "2026-09-23T06:42:42.482Z",
"sourceObservationWindow": {
"earliest": "2026-09-23T06:42:42.482Z",
"latest": "2026-09-23T06:42:42.482Z",
"validCount": 1,
"totalRowCount": 1
},
"evidence": [
{
"field": "citationCoverage",
"value": {
"direction": "backward",
"observed": 19
},
"rule": "observed_arrays_only",
"sourceRowIndex": 0
},
{
"field": "citationCoverage",
"value": {
"direction": "forward",
"observed": 55
},
"rule": "observed_arrays_only",
"sourceRowIndex": 0
}
],
"businessPartial": false,
"sourceRowIndexes": [
0
],
"partial": false,
"sourceCoverage": {
"complete": true,
"reason": null,
"droppedOversized": 0
},
"product": "patent-citation-overlap-analyzer",
"schemaVersion": "1.0",
"sourceDatasetId": null,
"sourceRunId": null,
"suppliedSourceRunId": null,
"sourceRunIdVerified": null,
"processedAt": "2026-09-23T08:20:43.471Z"
},
{
"entityId": "patent:US10000001B2",
"found": true,
"status": "citation_overlap_assessed",
"action": "no_overlap_in_batch",
"confidence": "high",
"sourceRowCount": 1,
"patentId": "US10000001B2",
"backwardCitationCount": 20,
"forwardCitationCount": 6,
"backwardSourceReportedTotal": 20,
"forwardSourceReportedTotal": 6,
"backwardCoveragePartial": false,
"forwardCoveragePartial": false,
"selfCitationInBackward": false,
"selfCitationInForward": false,
"topOverlappingPeers": [],
"overlappingPeerCount": 0,
"overlappingPeerCountIsLowerBound": false,
"peerEvidenceOmitted": 0,
"recordConflicts": [],
"recordConflictCount": 0,
"recordConflictCountIsLowerBound": false,
"observedAt": "2026-09-23T06:42:46.090Z",
"sourceObservationWindow": {
"earliest": "2026-09-23T06:42:46.090Z",
"latest": "2026-09-23T06:42:46.090Z",
"validCount": 1,
"totalRowCount": 1
},
"evidence": [
{
"field": "citationCoverage",
"value": {
"direction": "backward",
"observed": 20
},
"rule": "observed_arrays_only",
"sourceRowIndex": 1
},
{
"field": "citationCoverage",
"value": {
"direction": "forward",
"observed": 6
},
"rule": "observed_arrays_only",
"sourceRowIndex": 1
}
],
"businessPartial": false,
"sourceRowIndexes": [
1
],
"partial": false,
"sourceCoverage": {
"complete": true,
"reason": null,
"droppedOversized": 0
},
"product": "patent-citation-overlap-analyzer",
"schemaVersion": "1.0",
"sourceDatasetId": null,
"sourceRunId": null,
"suppliedSourceRunId": null,
"sourceRunIdVerified": null,
"processedAt": "2026-09-23T08:20:43.471Z"
}
]

Key-value store OUTPUT / SUMMARY record for this run:

{
"schemaVersion": "1.0",
"product": "patent-citation-overlap-analyzer",
"outcome": "delivered",
"errorCode": null,
"fatalFailure": false,
"deliveryUncertain": false,
"replayBlocked": false,
"requested": 2,
"usefulResults": 2,
"delivered": 2,
"paid": 2,
"free": 0,
"failed": 0,
"sourcePartial": false,
"sourcePartialReason": null,
"sourceDroppedOversized": 0,
"budgetStopped": false,
"resultsCapped": false,
"businessPartialResults": 0,
"oversizedDiagnosticsDropped": 0,
"diagnosticsTruncated": false,
"partialOverall": false,
"budgetMode": "finite",
"maxTotalChargeUsd": 0.1,
"replaySafety": "A new run is new processing and can bill again; this run does not deduplicate against prior runs.",
"businessSummary": {
"rowsProcessed": 2,
"resultsCount": 2,
"diagnosticsCount": 0,
"uniquePatents": 2,
"patentsCapped": false,
"patentsExcludedCount": 0,
"observedAt": null,
"sourceDatasetId": null,
"sourceRunId": null
},
"processedAt": "2026-09-23T08:20:43.630Z"
}

Scenario 2 -- partial coverage (one direction genuinely unknown)

Takes the first real source row and removes its citationsForward array and citationsForwardTotal entirely to test missing-direction handling. This is a deliberately reduced projection, not a claim that the upstream scraper returned that gap. Remaining values are unchanged from the actual source.

The partial test used the first source row from Scenario 1, with citationsForward and citationsForwardTotal deliberately removed; all retained values were unchanged.

Delivered Dataset row -- forwardCitationCount and forwardSourceReportedTotal are null (unknown), never 0; confidence drops to "medium" because one direction is entirely unknown; partial: true and businessPartial: true both surface the gap; the row is still found: true and still billed (1 of 1 result-found event on this run):

[
{
"entityId": "patent:US10000000B2",
"found": true,
"status": "citation_overlap_assessed",
"action": "no_overlap_in_batch",
"confidence": "medium",
"sourceRowCount": 1,
"patentId": "US10000000B2",
"backwardCitationCount": 19,
"forwardCitationCount": null,
"backwardSourceReportedTotal": 19,
"forwardSourceReportedTotal": null,
"backwardCoveragePartial": false,
"forwardCoveragePartial": false,
"selfCitationInBackward": true,
"selfCitationInForward": false,
"topOverlappingPeers": [],
"overlappingPeerCount": 0,
"overlappingPeerCountIsLowerBound": false,
"peerEvidenceOmitted": 0,
"recordConflicts": [],
"recordConflictCount": 0,
"recordConflictCountIsLowerBound": false,
"observedAt": "2026-09-23T06:42:42.482Z",
"sourceObservationWindow": {
"earliest": "2026-09-23T06:42:42.482Z",
"latest": "2026-09-23T06:42:42.482Z",
"validCount": 1,
"totalRowCount": 1
},
"evidence": [
{
"field": "citationCoverage",
"value": {
"direction": "backward",
"observed": 19
},
"rule": "observed_arrays_only",
"sourceRowIndex": 0
},
{
"field": "citationCoverage",
"value": {
"direction": "forward",
"observed": null
},
"rule": "observed_arrays_only",
"sourceRowIndex": null
}
],
"businessPartial": true,
"sourceRowIndexes": [
0
],
"partial": true,
"sourceCoverage": {
"complete": true,
"reason": null,
"droppedOversized": 0
},
"product": "patent-citation-overlap-analyzer",
"schemaVersion": "1.0",
"sourceDatasetId": null,
"sourceRunId": null,
"suppliedSourceRunId": null,
"sourceRunIdVerified": null,
"processedAt": "2026-09-23T08:22:07.531Z"
}
]

Key-value store OUTPUT / SUMMARY record for this run:

Captured run summary: outcome delivered; requested 1, useful 1, delivered 1, confirmed paid 1, free 0, oversized-result drops 0. The full summary uses the same field contract shown in Scenario 1.

Scenario 3 -- run charge limit below the price of one assessment

Same real two-patent input as Scenario 1 (omitted here -- see Scenario 1's input block above), but this run's Max total charge was set to $0.005 -- below the $0.04 list price of one assessment. The billing guard checks affordability before reading the source, so no source read and no transform pass happen; the run still writes one free diagnostic row so the Dataset is never silently empty.

Delivered Dataset row -- a single free budget_stopped diagnostic, found: false, zero result-found events charged:

[
{
"product": "patent-citation-overlap-analyzer",
"schemaVersion": "1.0",
"status": "budget_stopped",
"reason": "Run charge limit cannot cover any result; no source was read.",
"found": false,
"processedAt": "2026-09-23T08:21:42.634Z"
}
]

Key-value store OUTPUT / SUMMARY record for this run (paid: 0, free: 1, budgetStopped: true):

Captured run summary: outcome budget_stopped; requested 0, useful 0, delivered 0, confirmed paid 0, free 1, oversized-result drops 0. The full summary uses the same field contract shown in Scenario 1.

Scenario 4 -- empty source batch

An explicitly empty items array. The Actor never fabricates a placeholder result -- it writes exactly one free diagnostic explaining that no usable rows were present, so a genuinely empty batch is never confused with a failed run.

Input:

{
"items": [],
"maxResults": 2,
"maxInputRows": 100
}

Delivered Dataset row -- a single free no_results diagnostic, found: false:

[
{
"product": "patent-citation-overlap-analyzer",
"schemaVersion": "1.0",
"status": "no_results",
"found": false,
"reason": "no_usable_rows_in_batch",
"sourceCoverage": {
"complete": true,
"reason": null,
"droppedOversized": 0
},
"processedAt": "2026-09-23T08:21:35.512Z"
}
]

Key-value store OUTPUT / SUMMARY record for this run (paid: 0, free: 1, outcome: "no_results"):

Captured run summary: outcome no_results; requested 0, useful 0, delivered 0, confirmed paid 0, free 1, oversized-result drops 0. The full summary uses the same field contract shown in Scenario 1.

Field dictionary

Identity and status

FieldMeaning
entityIdpatent:<patentId> -- stable identity key for this assessment.
foundtrue for a useful, billable assessment; false for every free diagnostic row.
statuscitation_overlap_assessed for a useful row; otherwise a diagnostic code (missing_patent_id, missing_citation_data, source_row_failed, invalid_row, excluded_beyond_max_patents_bound, budget_stopped, max_results_reached, no_results, result_dropped_oversized, partial_source, source_rows_dropped).
actionreview_overlap when at least one overlapping peer exists in the batch; no_overlap_in_batch when coverage was checked and nothing shared was found. Never a legal or investment recommendation.
confidencehigh -- both directions known, no coverage-partial total, no conflict. medium -- only one direction known, or a gap/conflict exists. Diagnostics do not imply a confidence rating. Read partial alongside confidence; coverage omissions can matter even when both directions exist. This is evidence-coverage confidence, never a legal or probability claim.
partialUnified flag: true if the business result, the source read, or any oversized-row drop was partial.
evidenceBounded, ranked { field, value, rule, sourceRowIndex } array backing this assessment; includes direction-specific citationCoverage entries, each linked to the row supplying its selected set, even when no overlapping peer was found.
sourceRowIndexes / sourceRowCountOriginal dataset/inline row offsets feeding this assessment (capped at 50 shown) and the true total count.
sourceCoverageRun-level { complete, reason, droppedOversized } -- whether the source read itself was complete, independent of this patent's own data quality.
observedAt / processedAtobservedAt is the latest valid scrapedAt/checkedAt capture across this patent's accepted rows -- a source capture instant, never a patent filing/grant/publication date, never "now." processedAt is when this run wrote the row.

Domain fields

FieldMeaning
patentIdUppercase-normalized publication ID: country prefix (or supported JPS/JPH/JPWO forms), optional US RE/PP/D series, digits, letter-led kind code. A syntax check only, not proof of registry existence. Kind codes stay distinct -- US...A1 and US...B2 are never merged.
backwardCitationCount / forwardCitationCountDistinct valid IDs observed. null when the field was never a usable array on any row for this patent (unknown); 0 when the source explicitly reported an empty array (observed zero).
backwardSourceReportedTotal / forwardSourceReportedTotalLargest non-negative integer total reported, or null if never reported. Disagreements stay visible in recordConflicts; the maximum is used only as a conservative coverage signal.
backwardCoveragePartial / forwardCoveragePartialtrue only when a known reported total exceeds the observed array size -- the source itself disclosed a gap. false when either value is unknown.
selfCitationInBackward / selfCitationInForwardtrue when the patent's own ID appears inside its own citation array -- reported as-is, never silently corrected.
topOverlappingPeersCap-10 list of { peerPatentId, backwardIntersectionCount, backwardUnionCount, forwardIntersectionCount, forwardUnionCount, sourceRowIndex }, ranked by combined intersection size then peerPatentId. A null field means that direction was unknown for one side of that pair -- never a fabricated 0.
overlappingPeerCount / overlappingPeerCountIsLowerBound / peerEvidenceOmittedExact peer count in the bounded batch (IsLowerBound is always false -- it is exact, not an estimate of the real-world graph), and how many exact peers are omitted from the capped display list.
recordConflicts / recordConflictCount / recordConflictCountIsLowerBoundExplicit disagreements across duplicate rows for the same patentId (citation set or total), each naming field, the disagreeing values, and involved sourceRowIndexes. Capped at 10 (in practice at most 4, one per conflict-eligible field).
sourceObservationWindow{ earliest, latest, validCount, totalRowCount } -- raw source capture strings across accepted rows, never re-derived.

OUTPUT / SUMMARY key-value record

FieldMeaning
outcomedelivered, max_results_reached, budget_stopped, no_results, diagnostic, or failed.
requested / usefulResults / delivered / paid / free / failedRows read, rows qualifying as useful, rows written, confirmed paid result-found events, free diagnostic writes, and oversized-result drops.
sourcePartial / sourcePartialReason / sourceDroppedOversizedWhether the source read was incomplete, why, and how many oversized source rows were skipped.
budgetStopped / resultsCappedWhether Max total charge or maxResults stopped delivery before all useful rows were sent.
businessPartialResults / partialOverallCount of product-level partial rows; whether any partiality signal fired in the run.
replaySafetyStates plainly that a new run is new processing and can bill again; no cross-run deduplication.
businessSummaryDomain tallies: rowsProcessed, resultsCount, diagnosticsCount, uniquePatents, patentsCapped, patentsExcludedCount, sourceDatasetId, sourceRunId.

Evidence and boundaries

  • Directions never merge. Backward and forward counts, totals, coverage-partial flags and intersection/union sizes are always two independent metrics per patent, per peer -- never a single similarity score.
  • Intersection/union are exact set sizes over IDs present in both sides -- never estimated. A null value means one side of that directional pair was unknown, not that overlap was checked and found zero.
  • Null is never coerced to zero, and zero is never treated as null, anywhere in this output.
  • Kind codes are identity-significant. US...A1/US...B2 are separate identities; this Actor never resolves patent families or merges continuations/divisionals.
  • No prior-art, validity, infringement, or patentability conclusion is ever produced. action/confidence describe evidence coverage in this batch, never a legal or commercial verdict.
  • Overlap is batch-local. "Peer" means another patent in the same supplied batch -- never the patent's true, complete real-world citation graph. A different batch surfaces different peers.
  • Totals are used conservatively. A disagreeing total flags possible partial coverage only, never asserted as correct or most-recent; the disagreement stays visible in recordConflicts.
  • Conflict tie-break is stability, not truth. Disagreeing duplicate rows use the lexicographically-first sorted-ID-set signature purely for deterministic, order-independent metrics -- never a claim that observation is more correct or recent. The result is marked partial.
  • Capture window, not a lifecycle date. observedAt/sourceObservationWindow describe when the source scrape captured data (scrapedAt/checkedAt), strictly distinct from filing, priority, publication, or grant date; "now" is never fabricated.
  • Self-citations are reported, never filtered.
  • Explicit bounds: up to 1000 distinct patents enter pairwise comparison per run (alpha-sorted; excess reported via excluded_beyond_max_patents_bound, never silently dropped from the tally); up to 10 peers displayed per patent; up to 50 source row indexes displayed per result (true sourceRowCount reported separately).
  • confidence is coverage confidence, never legal accuracy or real-world completeness.

Decision routing

  • found: false -- this row is a diagnostic, not a citation assessment; inspect its reason because source data may be insufficient or the run may have hit a bound. Fix the source data or run settings and re-run; never read this as "checked, no overlap."
  • found: true, action: "no_overlap_in_batch", confidence: "high" -- both directions fully known and consistent, no shared citation ID with anything else supplied. This means no shared ID was observed in usable directional sets. Missing peer directions are still unknown, so it is not a complete negative for those comparisons; widen or refresh the input when necessary.
  • found: true, action: "review_overlap" -- at least one peer shares a citation ID. Route to a human reviewer; open topOverlappingPeers for which peers and how strong per direction, and use each peer's sourceRowIndex to trace back to the original scraped row.
  • confidence below high, or partial: true -- treat as directionally useful but incomplete. Check backwardCoveragePartial/forwardCoveragePartial and recordConflicts before acting; consider re-scraping the missing direction.
  • recordConflictCount > 0 -- duplicate source rows disagreed. Inspect the listed sourceRowIndexes in the original scraper output before trusting the numbers for that patent.

Commercial playbooks

  • Pre-diligence citation triage. Before a licensing/acquisition target's patents go to outside counsel, scrape the target's and your own relevant patents into one batch and run this Actor first. Route review_overlap + confidence: "high" rows to an analyst; treat lower-confidence/partial rows as "re-scrape first."
  • Competitive citation monitoring. Re-scrape a competitor watchlist on a schedule and re-run this Actor against each fresh batch. No cross-run deduplication exists (see Operating guide), so pair this with your own run-history tracking to diff new peer relationships.
  • Docketing intake triage. For newly filed/granted patents in a docket, flag which filings already share references in the observed citation sets before a paralegal manually cross-references citation lists.
  • Scraper data-quality audit. A sudden rise in partial rows or recordConflicts across a batch is itself a signal that the upstream scrape configuration or target pages changed, independent of any business use case.
  • Dashboard/report feed. Pipe the Dataset into a BI tool via CSV/JSON export or the API (below) to maintain a running, evidence-linked overlap table without hand-built set-intersection code.

Integration recipes

Apify API. Send the token as an Authorization: Bearer header -- never a URL query parameter. Always set a bounded maxTotalChargeUsd. This is a template, not a captured live call:

POST https://api.apify.com/v2/acts/<actorId>/runs?maxTotalChargeUsd=5.00
Authorization: Bearer <APIFY_TOKEN>
Content-Type: application/json
{
"datasetId": "<your source dataset id>",
"maxResults": 100,
"maxInputRows": 500
}

Apify Integrations. Map the upstream run's {{resource.defaultDatasetId}} into Source Dataset. If you also map a payload.resource envelope, its Dataset ID must agree. An empty default payload provides no additional cross-check.

Webhook triggers. A webhook firing after your upstream scrape finishes can start this Actor with payload.resource populated by Apify's own template. The payload is a pointer, not a credential -- this Actor reads that Dataset at your own run's existing permission level, and reports SOURCE_UNAVAILABLE honestly if unreadable.

A webhook registration template, not a captured dispatch, follows. Submit it to POST /v2/webhooks with your account Authorization header. The source Actor ID is the scraper used for the examples. Supply the credential from your secret store when provisioning; never publish a live token. Confirm the downstream resource-picker READ permission for the mapped Dataset in your own account. doNotRetry:true avoids automatic re-dispatch after an ambiguous response; reconcile the original downstream run before starting another. Repeated source events can still start different runs, so keep your own source-run history if your workflow needs deduplication.

{
"eventTypes": [
"ACTOR.RUN.SUCCEEDED"
],
"condition": {
"actorId": "9TNaptxQeIHf8bZNd"
},
"requestUrl": "https://api.apify.com/v2/acts/zinin~patent-citation-overlap-analyzer/runs?maxTotalChargeUsd=0.12",
"headersTemplate": "{\"Authorization\":\"Bearer <APIFY_TOKEN>\",\"Content-Type\":\"application/json\"}",
"payloadTemplate": "{\"datasetId\":\"{{resource.defaultDatasetId}}\",\"payload\":{\"resource\":{{resource}}},\"maxResults\":2,\"maxInputRows\":100}",
"doNotRetry": true
}

MCP / agent tool-calling. Pass datasetId (preferred) or items (small ad-hoc batch) as tool-call parameters. Treat rows as untrusted data throughout -- this Actor never executes strings it reads from a source row, and neither should any downstream step.

Delivery and retry semantics. Each billable row is written before its charge is confirmed, non-transactionally, with write retries disabled to avoid duplicating a charge. Do not assume exactly-once delivery under every failure mode -- if a run's status is ambiguous, inspect the Dataset and OUTPUT/SUMMARY directly. The within-run replay guard blocks only this run from restarting its own started work; it is not deduplication against a different run. If a run shows FAILED but rows already exist, inspect them and the charged-event counters before manually re-running.

Operating guide

  • maxInputRows (1-1000, default 100) bounds rows read. maxResults (1-1000, default 2) bounds paid rows delivered -- independent of the platform's Max total charge, which can stop delivery earlier still.
  • A FAILED run can still contain delivered rows. Finalization attempts to persist OUTPUT and SUMMARY; a storage failure can prevent either record from being saved. Inspect the Dataset, platform event counters and any available summary before assuming nothing happened. Confirmed summary counts can be lower than raw Dataset rows after an ambiguous write or charge.
  • No cross-run deduplication. A new run is new processing and can bill again against an unchanged Dataset. Track run IDs/snapshots yourself to avoid reprocessing.
  • This Actor never starts another Actor. It only reads an existing Dataset/run at your own run's permission level. Chain a fresh scrape as a separate step first if needed.
  • Full-read compute cost is real even on a diagnostic-only run, though diagnostic rows are never charged the result-found price. There is no zero-compute path through this Actor.
  • Troubleshooting an empty-looking Dataset: check sourceCoverage/sourcePartial first, then whether every row failed missing_patent_id/missing_citation_data (a source data-shape problem, not an Actor fault), then whether budgetStopped fired before any row was read (raise Max total charge and re-run).

FAQ

Does "no overlap" mean nothing useful was delivered, or I should not be charged? No. action: "no_overlap_in_batch" with confidence: "high" means the Actor fully checked this patent's coverage and honestly found no shared citation ID -- a real, billed fact. Missing usable directions, invalid identifiers, explicit source failures, empty input and budget/cap notices produce free diagnostics rather than citation assessments.

Why is overlappingPeerCount sometimes higher than the peers I can see? topOverlappingPeers is capped at 10 for a readable list. overlappingPeerCount is always the exact count found; peerEvidenceOmitted tells you exactly how many are not shown.

Will this find a patent's true, complete citation network? No. Overlap is computed only against the other patents in your supplied batch -- never the patent's real-world citation graph beyond it.

Can I trust backwardSourceReportedTotal as a live, authoritative count? Treat it as a conservative coverage signal from the scrape, not a live registry lookup. Disagreements across duplicate rows are always visible in recordConflicts.

What if I point this Actor at a Dataset I lack permission to read? It fails honestly with SOURCE_UNAVAILABLE rather than falling back to demo rows. A webhook payload never grants access beyond your own run's token.

Does re-running the same batch save money the second time? No. Every run is independent processing with no cross-run deduplication; an unchanged batch can be billed again in full.

Why does a self-citing patent still count itself in its own total? Because that is what the source data actually contained. selfCitationInBackward/selfCitationInForward surfaces this as an explicit anomaly instead of silently excluding it.

Sources and rights

This Actor processes rows you already have read access to -- typically output from a Google-Patents-style scraper such as khadinakbar/google-patents-scraper against public Google Patents pages. It never scrapes any website itself. The supplied example rows contain googlePatentsUrl; the output links back by patent ID and source row index rather than copying that URL into every assessment.

Related public Apify Store listings for a patent pipeline:

This Actor makes no legal claim about patent validity, ownership, licensing status, or infringement, and no output field is legal advice. patentId syntax validation checks ID shape only -- never proof of registry existence. Public examples omit personal names, addresses, dates of birth, and nationality; the underlying fields (assignee company names, filing/grant/publication dates, CPC codes) are public bibliographic patent data, not personal data.