AgentLinkOps / Methodology / Benchmark
Verifier benchmark, September 11, 2026.
This is an account of one run, on one date, against one target domain. It reports what the verifier concluded, what it could not conclude and why, how the answers were checked, the defect the run exposed, and what changed in the verifier afterwards. Each number keeps its denominator and its date. None of them is a property of the product as a whole, and the last section says what the run cannot show.
What was checked
On September 11, 2026 the verifier fetched 1,922 real pages across 1,888 distinct hosts. Every page came from one of our own link campaign registries for a single target domain, in domain scope: pages the campaign had targeted or earned. Fetches were read-only, one request at a time per origin, with each host’s robots file applied. The run took 210 seconds and downloaded 272 MB.
The sample is a link-building prospect list and a social-profile footprint. Login walls, CDN blocks and JavaScript profile pages are over-represented in it, and ordinary editorial pages are under-represented. The record calls it the hardest page population we own and asks that every rate be read as a floor, including the share that concluded.
The verifier that ran is the module the hosted service and the desktop CLI share, described on the methodology page. It reads static HTML, extracts each anchor as an occurrence and never renders JavaScript. It has changed since the run in the ways recorded below, and those changes are dated too.
Outcomes and denominators
A check ends in one of four states. Present and absent are conclusions about the page that was read. Source unavailable is a conclusion that the page itself could not be served. Unknown is a refusal to conclude, with a named reason. The run recorded these counts:
| State, as recorded on September 11, 2026 | Pages | Share of 1,922 |
|---|---|---|
| Present | 16 | 0.8% |
| Absent | 1,389 | 72.3% |
| Source unavailable | 46 | 2.4% |
| Concluded (the three rows above) | 1,451 | 75.5% |
| Unknown, with a named reason | 471 | 24.5% |
A correction travels with this table. Two of the sixteen present results were wrong, for one reason described below. The verifier was fixed the same day, and those two URLs now return unknown with the reason source_redirects_to_target. The run was not repeated, so the table shows the counts as recorded. Applying the fix to those two cases, the same results would read 14 present, 1,449 concluded (75.4%) and 473 unknown (24.6%). That is arithmetic on the record, not a new measurement.
Unknowns by reason
An unknown is never one thing. The record splits the 471 into classes because each class implies a different product decision, and one 24.5% figure would hide all of them.
| Why the page could not be concluded | Pages | Share of 1,922 | What it implies |
|---|---|---|---|
| Refused by our own robots policy | 174 | 9.1% | We chose not to fetch. Nothing a browser could buy |
| Transport or network | 87 | 4.5% | A retry may help; a browser will not |
| HTTP status (403, 429, other 4xx) | 83 | 4.3% | The publisher blocks non-browser clients |
| Login wall | 78 | 4.1% | A browser does not have the password either |
| Needs a browser | 42 | 2.2% | The render allowance, discussed below |
| Other | 7 | 0.4% |
The most frequent single reasons were a possible login wall (78 pages), a 403 on the page (76), a 403 on the robots file (65), a robots disallow rule (62), a page that appears to need rendering (40), a network error (35) and a robots file served as HTML (31). The reason robots_crawl_delay_requires_scheduler appears zero times because the run supplied a pacing hook; without one, the verifier refuses every host that states a crawl delay, and that would have counted our own omission as an error rate.
Two yardsticks, because one would be the verifier grading itself
Checking a parser’s answer by re-reading the page with the same parser reports zero errors on any parser, including a broken one. So the run used two independent references.
A second program reading the same bytes. A regular-expression sweep for anchor href values pointing at the target host, with comments and script bodies removed. It is crude, and it is wrong in different ways than a parse tree walk, which is the point. It could be applied to the 1,405 pages whose bytes were read.
| Verifier against the raw-bytes reference, 1,405 pages read | Pages |
|---|---|
| Both say present | 14 |
| Both say absent | 1,389 |
| Verifier absent, raw bytes carry the link (false absent) | 0 |
| Verifier present, raw bytes carry no link (false present) | 2 |
| Target named on the page without a link to it | 8 |
Zero false absents across 1,405 pages is the number that matters most for a link checker, because telling a customer a link is gone when it is present is the failure that costs the account. The eight pages that named the target without linking to it are worth a sentence: a plain string search would have reported every one of them as a backlink.
A human-labelled set. The second reference is the 56 pages a person had already labelled in the registry. It is small, and it is the only ground truth that exists for this sample.
The human-labelled set: 56 pages
The registry recorded 44 of those pages as live and 12 as not live: six where the link was known to be missing and six where the page was known to be dead. Here is how the verifier read them.
| Human label | Pages | Present | Absent | Source unavailable | Unknown |
|---|---|---|---|---|---|
| Live | 44 | 14 | 15 | 1 | 14 |
| Link missing | 6 | 0 | 2 | 0 | 4 |
| Dead | 6 | 0 | 0 | 3 | 3 |
What the human-labelled set can establish: none of the 12 pages a human had labelled not live was reported present. What it cannot establish is an accuracy rate. Fifty-six pages is too few, the labels were made on earlier dates than the run, and the set was not drawn to be representative of anything beyond this one registry.
The 15 live pages that came back absent deserve their own line, because they are the case a static fetch cannot handle. The raw-bytes reference agrees that the served HTML carries no anchor to the target, so the parser missed nothing. Re-fetching all fifteen and looking for the target showed three pages that carry it only inside a script, six where it appears nowhere in the served bytes because the page is a shell that loads its own content afterwards, and six where it appears in markup that is not a link, such as a meta tag, structured data or prose. Every one of them is a self-serve profile or content-host page that renders its one outbound link from JavaScript.
The verifier’s render-required rule fired on none of them, and could not have. That rule needs a page with no links and almost no text; a profile page is rich with its own navigation. Static HTML cannot separate “does not link to you” from “links to you through JavaScript” on a page like that. The product answer is an explicit per-check render with its own allowance, never a guess.
The defect the run found, and it was live
Two shortened links in the registry came back present with 120 occurrences each, the top one anchored “Skip to main content”. The verifier had followed the shortener’s redirect to the target domain, parsed the target’s own homepage and counted every internal link on it as a backlink from the shortener.
A URL that redirects to the target is not a page that links to the target. Left alone, any shortener pointed at any site would have registered as a backlink from that site, which is also how someone could have manufactured evidence. The fix: after the redirect chain resolves, a source that started elsewhere and now lands on the destination under the watch’s own scope returns unknown with the reason source_redirects_to_target, and no occurrences. The guard keys on the source having started somewhere else, because a customer checking internal links registers a source on their own domain, and a trailing-slash redirect inside that site must not turn every internal check into an unknown. Both cases have regression tests in the current source, and the two real URLs return the new reason.
The record keeps the defect alongside the fix, and this page does the same. The numbers above are the numbers of the run that found it.
A rule that was written and not shipped
After the adjudication, a rule was drafted: when the document names the target host and carries no readable link to it, refuse to conclude absence. On this corpus it reclassifies 8 of the 1,405 pages read, six of them known live. Excellent precision, on this sample.
It was not shipped. The same rule reclassifies two adversarial cases the verifier has deliberately committed to answering absent: a target URL sitting inside an HTML comment or a script string, and a look-alike host or an open-redirect parameter that merely contains the target. Two existing tests encode those as absent on purpose. Trading a stated guarantee for a heuristic is a product decision, so the record attached the measurement to it and left the guarantee in place.
Render escalation and the plan allowance
42 pages, 2.2% of 1,922, provably could not be read by a plain fetch: 40 that appear to need rendering and 2 whose navigation needs a browser. On top of that sits the profile-page class above, 15 pages or about 0.8% of the sample, which nothing can detect from static HTML.
Before this run, the included render allowance in the plan design was provisional at 1.67% of checks, marked as waiting for this number. On September 11, 2026 it was set to 3%, the 2.2% provable escalation plus the undetectable class, so that a watch does not return unknown because an allowance ran out. Plan allowances are internal planning that appear only in usage estimates. Payment collection does not exist in this build and no price on this site can be charged.
Nothing in the run measured rendering. The 2.2% is how often a plain fetch provably fails, not how often a browser would have succeeded. Only a render run answers that, and this run did not use one.
The robots posture at the time, and what changed after
174 pages were refused at the robots file, and 107 of them because the robots file could not be read at all: a 403 from a bot-blocking CDN on 65, a robots file served as an HTML shell on 31, and a scatter of other statuses. At the time of the run the verifier treated any unreadable robots file as a refusal. RFC 9309 reads a 4xx on the robots file as “unavailable”, which imposes no restriction, so the run’s posture was stricter than the standard.
Re-running exactly those 107 pages under the RFC reading concluded 21 of them; 53 of the rest returned a 403 on the page as well, because the CDN that blocked the robots file blocked everything. So the strict posture cost 1.1% of the sample, 21 pages of 1,922, and the record left the decision to the owner with that measurement attached.
The decision was made the same night. The current verifier reads a 4xx on the robots file, other than 429, as an unavailable file that imposes no restriction, and then fetches the page; a 429 or a 5xx on the robots file still refuses, because a temporarily unavailable file is the standard’s reason to stay away. A robots file served as HTML still refuses. The reporting improves with it: a page that then fails reads as source_http_403, the publisher refused the page, where before it read as robots_http_403, we never asked.
The paired recheck: did the changes help?
Four verifier changes landed that night, the robots reading among them. Re-running the whole benchmark and comparing rates would have been a weak test, because the web moves between runs and a rate difference could be the web rather than the code. Instead, 250 cases were drawn by a seeded sample from the original 1,922 and compared case by case against their own earlier result.
| Paired recheck, September 11, 2026, 250 cases | Count |
|---|---|
| Concluded before | 177 of 250 (70.8%) |
| Concluded after | 186 of 250 (74.4%) |
| Identical in state and reason | 230 |
| Moved | 20 |
| Moved from a conclusion to unknown, or flipped between present and absent | 0 |
Every one of the twenty started as unknown. Fifteen are attributable to the robots 4xx reading: eight moved from a robots 403 to a page 403 and stayed unknown with a truer reason, five concluded absent and one concluded source unavailable. One moved because robots group matching now works by prefix rather than substring; that publisher had never disallowed us, a group written for another crawler had matched inside our product token. Four moved on network variance between the two runs, not code. The comparison script that produced this exits non-zero on a lost conclusion or a changed conclusion, and was checked against a planted pair to make sure it can fail.
The recheck does not re-establish the headline numbers. The 75.5% and the zero false absents stand on 1,922 cases with an independent reference; the recheck is 250 cases with no reference, answering only whether the same cases now do better. Its 74.4% is a statistic on a different subset and should not be read against the 75.5%.
Time and bytes per check
| Cost shape of the run | Value |
|---|---|
| Time per page check, median | 981 ms |
| Time per page check, 90th percentile | 2.7 s |
| Time per page check, 99th percentile | 10.0 s |
| Bytes fetched, over the 1,532 pages that returned bytes | 272 MB, 182 KB mean |
These describe one machine on one day against one sample, with one request in flight per origin. They are not a service level.
Who is not named here
The internal record names the target domain, the registry it came from and the publisher hosts behind each adjudicated page. This page does not. The target belongs to one customer, and naming a customer needs that customer’s written permission, which is a separate decision. The publisher hosts are named in the record so a reader of the repository can reproduce the adjudication; on a public page they would read as a list of sites that failed a test they never agreed to take, and the point of the adjudication is the class of page, not the host. Shortened links are described by what they did, not by which service they used.
What this does not show
- No universal accuracy rate. Every figure on this page is a floor for one sample on one date. “Zero false absents” is true of 1,405 pages read on September 11, 2026, and of nothing else.
- No ranking effect. A present result is a link that existed on a page at a time. Nothing here connects a link to a position in any search engine, and nothing here predicts one.
- No whole-web coverage. The verifier checked pages it was given. It found nothing and claims to have found nothing.
- One target domain. Occurrence matching, scope handling and anchor extraction were exercised against a single domain.
- A small human-labelled set. Fifty-six labelled pages. Everything else was measured against a second program reading the same bytes, which cannot see a link that was never served.
- No rendering was measured. The 2.2% is the rate at which a plain fetch provably fails, not the rate at which a browser would succeed.
- No comparison with any other tool. No other checker ran on these pages. Dated comparisons of published behavior live on the compare pages, and they quote what vendors say, not what a run measured.
- Not a re-run. The verifier changed after this run. The paired recheck shows the changes did not regress 250 of these cases; it does not refresh the headline counts, and no later full run has been recorded.
How this page was checked
Written September 15, 2026 against the repository at commit af8f4ba. Each number quotes a row of the DP-0023 claims ledger: B-01 to B-11, with A-03, A-14, M-01 and W-01 for the product facts around them. The rows trace to the dated record of the run, its per-page results file, the report generated from that file, the adjudication and robots experiment files, and the paired recheck record and its result file, all kept in the repository. The redirect guard and the robots reading were re-read in the current verifier source on that commit, and the verifier suite was re-run the same day.
The reading rules that make these states mean what they mean are on the methodology page. What the states mean for a buyer, and the one comparison we make with read-only data servers, are on what your agent can prove. How to act on an unknown or an absent result in your own monitoring is on the backlink monitoring hub and in backlink missing but the page is live.
A changed verifier voids the parts of this account that describe current behavior until they are rechecked; the counts of the run itself do not change, because the run is dated.