Patent Citation Overlap Analyzer
Pricing
from $28.00 / 1,000 citation assessments
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
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.

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:
- Missing vs. zero. A field never a usable array on any accepted row is
null(unknown), never coerced to0. An array the source reported as empty ([]) is observed0. "We don't know" and "we checked and there are none" are never conflated. - Record conflicts. When duplicate rows for the same
patentIddisagree -- different citation sets or totals -- the assessment records the disagreement explicitly (recordConflicts) instead of silently picking one; the row is markedpartial. - 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).

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
partialandrecordConflicts).
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 viasourceCoverage/sourcePartialif 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-sortedpatentIds);maxResultsonly 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 againstmaxResultsand 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.
| Tier | Price 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
citationsBackwardnor a usablecitationsForwardarray 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:
- It collects
datasetIdandpayload.resource.defaultDatasetId. When both exist, they must identify the same Dataset. A Dataset reference, including one from the payload, takes precedence over inline rows. - It collects
runIdandpayload.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. - 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
| Field | Meaning |
|---|---|
entityId | patent:<patentId> -- stable identity key for this assessment. |
found | true for a useful, billable assessment; false for every free diagnostic row. |
status | citation_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). |
action | review_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. |
confidence | high -- 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. |
partial | Unified flag: true if the business result, the source read, or any oversized-row drop was partial. |
evidence | Bounded, 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 / sourceRowCount | Original dataset/inline row offsets feeding this assessment (capped at 50 shown) and the true total count. |
sourceCoverage | Run-level { complete, reason, droppedOversized } -- whether the source read itself was complete, independent of this patent's own data quality. |
observedAt / processedAt | observedAt 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
| Field | Meaning |
|---|---|
patentId | Uppercase-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 / forwardCitationCount | Distinct 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 / forwardSourceReportedTotal | Largest 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 / forwardCoveragePartial | true only when a known reported total exceeds the observed array size -- the source itself disclosed a gap. false when either value is unknown. |
selfCitationInBackward / selfCitationInForward | true when the patent's own ID appears inside its own citation array -- reported as-is, never silently corrected. |
topOverlappingPeers | Cap-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 / peerEvidenceOmitted | Exact 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 / recordConflictCountIsLowerBound | Explicit 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
| Field | Meaning |
|---|---|
outcome | delivered, max_results_reached, budget_stopped, no_results, diagnostic, or failed. |
requested / usefulResults / delivered / paid / free / failed | Rows read, rows qualifying as useful, rows written, confirmed paid result-found events, free diagnostic writes, and oversized-result drops. |
sourcePartial / sourcePartialReason / sourceDroppedOversized | Whether the source read was incomplete, why, and how many oversized source rows were skipped. |
budgetStopped / resultsCapped | Whether Max total charge or maxResults stopped delivery before all useful rows were sent. |
businessPartialResults / partialOverall | Count of product-level partial rows; whether any partiality signal fired in the run. |
replaySafety | States plainly that a new run is new processing and can bill again; no cross-run deduplication. |
businessSummary | Domain 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
nullvalue 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...B2are separate identities; this Actor never resolves patent families or merges continuations/divisionals. - No prior-art, validity, infringement, or patentability conclusion is ever produced.
action/confidencedescribe 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/sourceObservationWindowdescribe 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 (truesourceRowCountreported separately). confidenceis 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; opentopOverlappingPeersfor which peers and how strong per direction, and use each peer'ssourceRowIndexto trace back to the original scraped row.confidencebelowhigh, orpartial: true-- treat as directionally useful but incomplete. CheckbackwardCoveragePartial/forwardCoveragePartialandrecordConflictsbefore acting; consider re-scraping the missing direction.recordConflictCount > 0-- duplicate source rows disagreed. Inspect the listedsourceRowIndexesin 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/partialrows 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
partialrows orrecordConflictsacross 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.00Authorization: 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
FAILEDrun can still contain delivered rows. Finalization attempts to persistOUTPUTandSUMMARY; 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-foundprice. There is no zero-compute path through this Actor. - Troubleshooting an empty-looking Dataset: check
sourceCoverage/sourcePartialfirst, then whether every row failedmissing_patent_id/missing_citation_data(a source data-shape problem, not an Actor fault), then whetherbudgetStoppedfired 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:
khadinakbar/google-patents-scraper-- upstream scraper behind every real example here.zinin/patent-monitor-- public patent-monitoring Actor, useful for a recurring watchlist batch.- Company Registry Enricher -- inspect an assignee company separately when resolving a business identity; its registry records are not citation input.
- Funding Round Tracker -- a separate financing-evidence step for company research around a portfolio; funding records do not change citation metrics.
hanamira/patent-trademark-search-- public patent/trademark search Actor, useful for discovering candidatepatentIds.- Google Patents -- public source of record for the identifiers, kind codes, and citations this Actor normalizes.
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.