Docket / Learn / What Docket got wrong

What Docket got wrong, and when it was fixed

An audit tool that is wrong sends you after a problem you do not have. This page lists 10 releases of Docket, from 23 September 2026 to 21 October 2026, that fixed something it got wrong. Each entry names the release and the day it was published, says what was wrong in plain words, how we found it, what changed, and which tests now hold the fix in place. Where we could not prove something, the entry says so.

This is the part of our log that has shipped as a release. It is not a list of every mistake, only of the ones we found and fixed. For the questions to ask of any tool's report, ours included, see how to tell whether an audit tool is lying to you; it counts 195 of Docket's 551 test files that record a past mistake in their own words. After the releases, 15 worked cases show what such mistakes look like from the reader's side.

How to check an entry

The release link under each heading goes to the public release on GitHub, which shows the day it was published. The test files are the 20 files in Docket's own suite that pin these fixes; their names are given so you can see what each one is about. Speed-ups and new features are not listed, except where the slow path was leaving results out.

A Cloudflare site's bot check was reported as AI crawlers refused

Release 1.3.112, published 21 October 2026. The release on GitHub.

What was wrong. Docket asks a site's server for the homepage as each AI crawler, from the user's own computer. Cloudflare checks real crawlers by the network address they come from, so to Cloudflare those requests are crawlers pretending, and it may challenge or turn them away while serving the real crawlers normally. Docket reported that as AI crawlers refused by the server, on two sites at critical severity and with a warning that search was blocked too, although the real crawlers read both. A server that asked a crawler to pay, with HTTP 402, was counted as having served the page.

How we found it. Before any ad said sites were blocking AI crawlers, we tested the check on twenty sites behind Cloudflare and compared each verdict with what each crawler is really served. Three sites block AI crawlers by name, and Docket was right about them; two were false blocks.

What changed. When Cloudflare challenges the request, or refuses Docket's Googlebot request as well, Docket now says it could not test those crawlers from here, and where the real answer is: Cloudflare's security events, and Search Console for Google. A rule that refuses AI crawlers by name while Googlebot is served is still reported as a block. A 402 is reported as a request to pay, never as a page served. The same release fixes 24 other wrong findings, found by auditing real sites of several kinds and reading every finding against the page.

Tests that hold it. test_a_crawler_cloudflare_cannot_verify_is_not_a_blocked_crawler.py, test_a_crawler_asked_to_pay_was_not_served_the_page.py

The installed app kept no record of what its helpers did

Release 1.3.110, published 14 October 2026. The release on GitHub.

What was wrong. The desktop app starts Docket's engine with its output going nowhere. If the helper that renders pages in a browser was missing or broken, nothing said so: rendering was skipped, the checks that need it reported that they could not confirm, and the report looked like an audit where rendering had simply found nothing. Starting the installed engine with an empty home folder left no log at all.

How we found it. Another of our apps found a doubling in speed by reading its log. Docket had no log to read, so a support question would have been answered from the customer's description of it.

What changed. The engine now writes a log file in the .docket folder in your home folder: the version, the licence mode (never the key), and for the render helper whether it was found, where, and whether it runs. It adds a line for each audit that names the host and nothing more. The file stays on your Mac and nothing sends it. The build now starts the finished app with an empty home folder and refuses to ship unless the log says the render helper runs. It then points the engine at a file that cannot run and requires the log to say so.

Tests that hold it. test_the_installed_engine_keeps_a_log_that_names_its_helpers.py

Five wrong findings, found by auditing our own sites

Release 1.3.107, published 13 October 2026. The release on GitHub.

What was wrong. Docket read a date followed by a sentence ("last updated 2026-09-15 The fastest way:") as the street address "15 The fastest way". That made a Mac app a local business, and its two top findings were high severity: no phone number and no LocalBusiness markup. It read a licence date followed by a year range as a phone number. It treated the home city in a person's markup as a business address. It counted pages whose canonical tag names a different page as competing with that page. And one index page could pull a whole series into one "target the same search" group: a page for each of 29 states and their index counted as one search.

How we found it. We audited all seven of our own sites and read every finding against the live page. Five findings were Docket's mistake, not the site's.

What changed. A day in an ISO or written date, a month and year, and a run of years are no longer an address or a phone number. A person's address no longer makes a local business. A page that names another as its canonical stops competing with it. A section index no longer glues its pages into one group, and Virginia and West Virginia are two places.

Result. On the same crawl before and after, the app site's score went from 89.9 to 92.3 and its four local findings went. A portfolio site went from 91.8 to 98.0. The series site's competing-pages finding fell from 53 pages in 6 groups to 13 in 6; the pairs left are calculators that share a title ending and two overlaps that look real.

Tests that hold it. test_a_date_and_a_sentence_are_not_a_street_address.py, test_a_licence_date_and_a_year_range_are_not_a_phone_number.py, test_where_a_person_lives_is_not_a_local_business.py, test_a_page_that_names_another_as_its_canonical_does_not_compete_with_it.py, test_a_hub_does_not_glue_a_series_into_one_search.py

The link check looked at one host thirty times, and the topic step threw away its answers

Release 1.3.106, published 13 October 2026. The release on GitHub.

What was wrong. The outbound link check took the first 60 links in page order. On our own site 30 of its 61 targets were Buy and Download links to the same host, so the sample said little about the rest. Separately, the step that suggests topics asked a search engine's autocomplete for 18 commercial phrasings for each seed word and then discarded every answer. The stage between the link check and rendering took 30 to 34 seconds on the installed copy.

How we found it. Measuring the installed copy one stage at a time. With a counting wrapper on the same code, two seed words made 62 requests and spent 32.3 seconds in that step.

What changed. The sample takes at most 8 links to any one site, and the report says "at most 8 to any one site" so it does not claim to have checked more. The topic step now asks only for the phrasings it keeps.

Result. Side by side against the live search engine, the same seeds made 62 calls in 38.3 seconds before and 26 calls in 13.0 seconds after, and the suggestions kept were identical in both pairs of runs (112 and 212). Dropping the commercial phrasings could in principle hide a later question behind an earlier completion; we did not see it in either pair.

Tests that hold it. test_the_topic_suggestions_connector_asks_only_for_what_it_keeps.py, test_the_outbound_sample_is_spread_over_hosts_not_taken_in_page_order.py

Outbound links waited in line behind the audited site's pace

Release 1.3.105, published 13 October 2026. The release on GitHub.

What was wrong. Every outbound link check went through the gap meant to protect the site being audited, so 60 requests to 60 other servers queued behind a pace of four a second. The stage took 16.8 seconds of a 101-second audit. On a site whose robots.txt asked for ten seconds between requests it would have taken 600 seconds for 60 one-off links, which is why the check had a time budget that left links unchecked.

How we found it. Measuring the installed copy one stage at a time.

What changed. Each other host now gets its own clock and its own gap, so different hosts are checked side by side. The audited site's own links keep the polite gap. A link the clock had no time for is now left out of the results, not recorded as status 0, which the report would have called unreachable.

Result. Installed, the links stage took 11.0 and 10.4 seconds against 16.8, and the whole audit 96.9 and 96.7 against 101 to 105. It is not under 5 seconds because 30 of the 61 targets went to one host, which 1.3.106 addresses.

Tests that hold it. test_outbound_links_are_paced_per_host_not_by_the_audited_site.py

A page that followed Docket's own advice was still flagged

Release 1.3.104, published 10 October 2026. The release on GitHub.

What was wrong. The finding about old image formats prints a picture element with a WebP source and the JPEG kept as the fallback. A four-page test site with exactly that markup still had all 16 of its images reported as old formats, because the check read each image's src, and in that markup the src is the fallback by design. Following the tool's advice did not clear the tool's finding.

How we found it. Writing the how-to guide for that finding, we audited a four-page test site with plain image tags and again with the picture markup. Plain tags reported 16 images, correctly. The picture markup also reported 16.

What changed. An image in a picture element with a WebP or AVIF source, or with a srcset that names only such files, now counts as modern. A picture of JPEG sources, a mixed srcset and a plain image are still reported. JPEG XL is not counted: MDN, read on 10 October 2026, says its browser support is not yet universal.

The guide for that finding, which says the bug existed.

Tests that hold it. test_a_picture_with_a_webp_source_is_a_modern_image.py

A franchise and a church: four findings that were false

Release 1.3.103, published 9 October 2026. The release on GitHub.

What was wrong. On a plumbing franchise with a section for each location, Docket said 309 pages in 69 groups targeted the same search. They were the same services in different towns, each location writing its own template around the place name. It also told that site that marking up its reviews of itself would earn stars in search, which Google's review-snippet guidelines (read 9 October 2026) rule out for a business reviewing itself. On a church site it said there was no LocalBusiness markup on a local business, gave it a LocalBusiness block to paste, and told it that it had no customer reviews.

How we found it. Two offline audits of real sites, each finding read against the pages. The rest of what Docket reported on both sites was true.

What changed. A town, county or state named in a title now sets two pages apart, and a contact page no longer competes with a service page. Reviews count only on a page about a thing, such as a product, a recipe or an event. A place of worship is recognised from its name and is offered Church markup, and the no-reviews finding applies to businesses only.

Result. On the same crawl the franchise went from 309 pages in 69 groups to 13 in 6. The 13 include one town's service written twice, which do compete.

Tests that hold it. test_the_same_service_in_two_towns_is_two_searches.py, test_a_business_reviewing_itself_cannot_earn_stars.py, test_a_church_is_not_a_local_business.py

Refresh said everything was up to date over rows that were a week old

Release 1.3.102, published 9 October 2026. The release on GitHub.

What was wrong. Refresh answered "Up to date: 4 sites, nothing changed" while every row on the watch list read "7d ago" on a daily schedule. The button compared the lengths of two lists. The scheduler runs only while the app is open and took one overdue site a minute, so a copy that had been closed for a week was minutes from current and said nothing.

How we found it. From a screenshot of the watch list.

What changed. Refresh now checks every overdue site, one after another, and says how many it started. An overdue row says so, a check that failed shows its reason on its row, and the header says checks run while the app is open and catch up when it opens.

Not proven. We could not read the copy in the screenshot, so we do not know whether it had been closed or was failing. The new row text shows which.

Tests that hold it. test_pressing_refresh_on_overdue_sites_checks_them.py

A text-message number was called not clickable, and a business with no shop was asked for an address

Release 1.3.97, published 2 October 2026. The release on GitHub.

What was wrong. A business that publishes its number only as sms: links, by design, was told the number was not clickable and to wrap it in tel: links, which invites calls the business does not take. A service-area business that declares where it serves was told to put a street address in its footer, the opposite of Google's guidance for such a business.

How we found it. Both showed up in the top five findings of the business in our own public sample report. Read against its live pages, the home page had six sms: links, no tel: link and a declared service area.

What changed. sms: and smsto: links now count as click-to-contact, and where the finding still applies its fix offers an sms: link for a number that takes texts. A service-area business is no longer asked for an address. Both were tested in both directions: the real case is left alone and the same site without the sms: link, or without the service-area signal, is still reported.

Tests that hold it. test_a_text_only_number_and_a_service_area_are_not_gaps.py

Tags added by JavaScript were reported as missing

Release 1.3.80, published 23 September 2026. The release on GitHub.

What was wrong. Docket told a games publisher that analytics was missing from 30 of 30 pages and that no other analytics ran anywhere. The site loads Tag Manager, GA4, Google Ads and the Meta pixel with JavaScript, so the served HTML had none of them and a browser had all of them. Two checks about JavaScript also flagged a framework's login page as invisible to AI crawlers and a password-reset page as very thin, though nobody searches for either.

How we found it. A sweep of well-built sites, with a deliberately defective control site that had to fire and did. Each high-severity finding was read against the live page. Most of the others were true: one browser maker's home page really does carry no structured data.

What changed. When a render finds tags on a page whose served HTML showed none, the served-only gap is stood down. Login and password pages are skipped by both JavaScript checks. The release also fixed a case where a render cleared the flag on a page that is an empty single-page-app shell, which is still one word of content after rendering.

Not proven. One payments company's very-thin finding could not be rechecked that day, because the audit refused to run while a build was in progress. It was carried to the next round.

Tests that hold it. test_a_tag_the_render_found_is_not_a_gap.py, test_a_login_page_is_not_owed_to_ai_crawlers.py, test_a_short_page_is_not_a_javascript_page.py

15 cases where a finding looked wrong

The releases above say what changed. These 15 cases say what the mistake looked like to the person reading the report, and what to check on your own. They were first published as separate guides and are collected here. They are not release records: none carries a release number or a test list, because the guides never claimed either. No site is named.

A number that counted the wrong thing

A finding's count is not decoration

On a hospital's report, a finding's badge read sixteen profiles directly above the headline “Only two recognised profiles in sameAs”. The headline counted the profiles the site had. The badge counted the ones it did not: the rest of the tool's own list. That is more than a wording problem, because the count feeds the score. The deduction grows with the logarithm of the count, so the inflated badge lifted a low severity finding above the medium one for having no sameAs at all.

The phone check had the same fault in another form. It counted links, not pages, so one bad number in a header and a footer became two pages, with “and one more” printed under a list that showed the only page there was. Both checks now count what their headlines count.

On your own report. Read the badge against the headline and the count against the URLs listed, then ask what one fix would cover. If one edit to one template clears every instance, the count is measuring evidence, not work.

Registered checks. ai.entity, cvr.unusable_phone.

See also: How findings are ordered.

When two findings count the same pages twice

On a shoe retailer's crawl of about thirty pages, the report carried two findings with the same check, the same fix and titles that differed only by a count of pages with incomplete product markup. Most pages sat under both, so the total was larger than the crawl. The findings were keyed on the type and the list of missing fields, so one type split into as many findings as it had field combinations. It is now one finding per type, with the combinations in the detail.

The explanation beside the count had two faults of its own. It asserted that a listing or variant picker repeats the type, and on a site where the blocks were references it sent readers hunting for something that was not there. And the sentence that does say the blocks are references was shown only when blocks outnumbered pages, so the ordinary shape of one reference per page never got it.

On your own report. Add the counts. If the total is bigger than the pages crawled, two findings overlap: compare their URL lists and their fixes before treating them as two jobs.

Registered check. schema.incomplete.

See also: An @id is a pointer, not a definition.

A pixel on part of the site, and on no pages

A financial institution's report listed six advertising pixels as installed on only part of the site, each with a page count of zero. A pixel on no pages is either absent or somewhere the check could not count, and here it was the second. The installed list merged the served HTML with the rendered page, so it saw tags that JavaScript adds. The per-page count read the served HTML alone, so the same tags scored zero, which is below every coverage threshold. The same seam kept a site that loads every tag through a container from ever reaching the warning about too many tools on one page.

Counting from the rendered pages instead would have been worse, because only a sample is rendered and “we only looked at a few pages” would become “it is only on a few pages”. Coverage is now judged only for tags visible in the served HTML, and the finding names the tags it did not judge.

On your own report. When a headline says part of the site and the number says zero pages, believe the number and distrust the headline. View source to see whether the tag is in the HTML at all, and ask the ad platform which pages fired its pixel.

Registered checks. mar.ad_pixels, mar.stack.

See also: The tracking checks.

When an audit blames the site for one template

A tool reported that a site had no share cards. The pages people actually share already had complete card markup; the ones without were a listing, an archive and other pages made by a different template. The count was accurate and the verdict was not. Nearly everything an audit measures is set once, in a layout, a partial or a component, so findings cluster by template, and a count with no location both overstates the work and understates the danger. A majority is not a site.

The check now tells every page from most of them. A tool should also give a proportion and name the URLs, so the grouping shows without an export.

On your own report. Sort the affected URLs by path: the grouping usually falls out at once. Then look at one page that was not flagged. If a page of the same kind is correct, the template was fixed once and not everywhere.

See also: How to tell whether an audit tool is lying to you.

A finding inferred from text, markup or a name

Why an audit finds reviews you do not have

A software foundation was told “Reviews are shown but not marked up”. The evidence was the word review used as an ordinary verb, and the site's own sentence saying it does not provide testimonials, counted as proof that it does. A wildlife charity's joke about five-star accommodation for bees was quoted back as displayed customer ratings. One mistake was ours alone: a street-address pattern matched “that way” in this project's own marketing copy, decided a Mac software company was a local business, and produced six findings, two of them false at high severity.

The phrase list is short and specific now, and the address pattern needs a capitalised street name, which knowingly misses some real addresses. The two errors are not equal. A missed review section gives advice you can ignore. A false match sends you looking for a section you do not have.

On your own report. Read the quoted evidence before the headline, and ask whether the page is about the thing or doing it. A policy page about reviews matches every phrase list and is not the signal.

Registered checks. local.reviews, local.applicable.

Telling your site's furniture from its content

Almost every judgement about what a page says depends on telling its furniture (navigation, header, footer, share buttons) from its content. We got it wrong five times in five checks, the fifth in the fix for the fourth. Counting links measures the template: an author archive carries dozens of links and only a few are the author's articles. The share of words inside links is better, until a page carries bare anchor tags with no navigation element around them. Then nothing is stripped, a genuine stub looks like an index, and an empty page is excused as an archive.

What separates a label from an entry is length: an anchor needs at least four words to count as an entry. So there are three tests, each failing somewhere: repetition across pages, position inside a navigation element, and length.

On your own report. When a tool calls a listing page thin, ask what it counted. If it counted links, it counted your template.

Registered check. content.thin.

See also: How to run a content audit.

Not every address on your site is yours

The check that tests whether published email addresses can receive mail has to decide which addresses are yours. A software forum's how-to contained example addresses at a documentation domain, which the platform turned into links, so the site was told to publish mail records for a domain it does not own. The finding was true and the instruction was impossible. The remedy is now offered only for a domain the reader can publish records for.

A law firm's share-by-email button has an empty recipient on purpose, but a branch tested the verdict and not the reason, so “not a domain” became “cannot receive mail”. That case is skipped, not renamed, because it is not an address. The case that must still fire is a bare server address, which can never have mail records.

On your own report. Look at the domain in the address. If it is not yours, no instruction about mail records applies to you.

Registered check. cvr.dead_contact.

See also: What a dead contact address is.

Which host is that finding about?

An audit said a streaming service's certificate was close to expiry. The crawl began at the www address and followed a redirect, and it was the destination host's certificate that was expiring. Checking the address that had been typed showed months left, so the sentence read as false. The finding now names the host.

The same shape appeared in two more places. A bakery chain was told its plain-http address did not redirect to https, true of the www spelling and not of the bare domain, which are separate hosts with their own certificates and redirect rules. And on a large retail site the plain-http address returned an error, which the report called an insecure copy of every page. Reachable, answering and refusing are three situations, and only the first is the problem it described.

On your own report. Find the host before you check the claim, and test both spellings over plain http by hand.

Registered check. security.https.

See also: How to redirect http to https, www or no www.

Structured data read as something it was not

An @id is a pointer, not a definition

An audit reported several pages as having incomplete Organization markup, but the Organization was defined once, with its name, URL and logo, and the flagged blocks were references to it by @id. Two nodes with the same @id are one node, and a bare reference is complete as written; the most used SEO plugins emit it by default. Reproduced with a controlled graph, the check counted the reference as a second, worse copy of the thing it pointed at.

The real defect looks the same from outside. A search engine reads one page at a time, so if every block of a type on a page is a bare reference and nothing there defines it, it resolves to nothing, and neither a validator nor a rich result test says so. The repair is to define the entity on every page that references it, or to remove the stub, not to add a name to each stub.

On your own report. View source on a flagged page and search for the identifier. If it appears once as a definition and once as a reference, your markup is fine and the finding is wrong.

Registered check. schema.incomplete.

See also: How to fix structured data errors.

The markup on your page may not be about you

An audit said a national food-delivery marketplace was missing its address, telephone, opening hours and price range, with two fields present but blank. The only structured data on the page was a list of restaurants, other people's takeaways, and the check had judged the first node it reached as if it belonged to the publisher. One property settles whose data it is: itemListElement, whose entries are the things a page lists, not the thing it is.

The finding stayed and changed its subject, because thin listing markup is real and a chain publishing its own branches in a list still needs complete entries. It now describes the businesses the page lists. A second sentence, that the tool had found restaurant markup, was literally true and read as “we think you are a restaurant”.

On your own report. Ask whose data is missing. If the finding names fields you would never publish about yourself, the node is probably not yours: search the page source for itemListElement.

Registered checks. local.schema, local.applicable.

See also: An @id is a pointer, not a definition.

A completeness check assumes your type is right

A property portal's homepage declared a Product node that held the company's name, address on the web, logo and profile links. It had no image, so the check advised adding one. The advice was correct for the type and the type was wrong: logo is not a property of Product, and with no offer, price or review the block was never going to produce a product rich result. A completeness check works by lookup, so the type is an input it never judges. It now speaks up only when an Organization-only property is present and every product signal is absent.

The same shape in miniature: a large public-sector site wrote headLine for headline. JSON-LD keys are case sensitive, so no headline was read at all. The check matches on case and never on spelling, because a confident wrong guess is worse than a generic sentence.

On your own report. Read the type before the missing fields, and search your block for the property without regard to case.

Registered check. schema.incomplete.

See also: How to fix structured data errors.

A decision called a defect, a fix with a cost, a gap nobody reported

What a dead robots.txt rule actually costs

A computer manufacturer's robots.txt named six retired AI crawler tokens, and we reported at medium severity that those crawlers were reading pages it meant to withhold. That was false. The catch-all group carried seventeen Disallow rules, and all seven in the AI group were repeated there. A crawler that matches no group falls back to User-agent: *, so these fell back to a stricter group and lost no coverage. Only a Crawl-delay went unheard.

A rule's effect belongs to the group, not to the token. A dead name costs nothing when the catch-all is stricter or carries the same rules, or when a live replacement sits in the same group. It is a real exposure only when the catch-all is looser. The comparison is an exact match on the rules, deliberately crude, so the uncertain case stays reported.

On your own report. Compare the AI group's rules with the catch-all's. If every rule also appears under *, the dead name has cost you nothing.

Registered check. ai.dead_crawler_directive.

See also: How often AI rules name a crawler that no longer answers, Which AI crawlers to block.

When a report calls your decision a defect

A national broadcaster names the AI crawlers that decide whether it can be cited and disallows them, on purpose, as editorial policy. We called that critical and capped its score. The opposite case writes the same line: a blanket User-agent: * rule added for an unrelated reason sweeps up every crawler, and nobody decided it. What separates them is whether the crawler is named.

Then a worse failure. For a file that named two crawlers and let a wildcard catch a third, the report said none of them was named, a sentence that set the severity and told the reader to re-allow two crawlers they had just blocked deliberately. The set is now split: named blocks are a notice, wildcard-caught ones are the interruption, and an allowlist that names the AI crawlers it admits counts as policy. The paste-ready block is derived from what was found.

On your own report. Open the file and look for the name. Check any report sentence that says none or every against the file itself, because that is where reports go wrong.

Registered check. ai.crawler_access.

See also: How to fix AI crawler access, A rate limit is not a block.

A fix that hid a real problem somewhere else

A preprint archive's logo link is named “archive home”. The brand-name check strips navigation words from such a name, so “home” came off and “archive” was recorded as a second name for the organisation. The fix made the weakest source, a link name, agree with the one signal a site cannot easily fake: its own domain. The first version asked that of every residue, and it broke a case a test in the suite holds, a logo naming one institution on a site titled after another, which is exactly the disagreement the check exists to find. One site's false positive had been traded for another site's false negative, and a false negative makes no noise.

The narrower condition applies only when a navigation word had to be removed, because that is what made the string suspicious. A company whose domain does not contain its name loses that one source in that case, and its declarations still count. A related guard: a name seen once across dozens of signals is a single odd page, not a second identity.

On your own report. When a false positive you reported gets fixed, ask what the fix cost. A condition that makes a rule quieter about you probably made it quieter about someone else.

Registered check. brand.name_consistency.

See also: Where the name guess comes from, Brand consistency.

When a report says nothing at all

Broken-page checks sort responses into buckets: server errors, not found and gone, refused requests, and no response at all. The rest of the four-hundred range, where the server understood the request and rejected it, was never asked about. So a URL on an audited site, linked from its own pages and rejected by its own server, produced no finding from any registered check. It was found by accident, while checking that a change to the external link rule had not silenced the internal one.

Nobody complains about silence. The page is not a not-found, so every report that counts those shows the site clean while a search engine gets the same refusal as the visitor. Statuses that mean the crawler itself was turned away, such as a rate limit, stay out on purpose.

On your own report. Compare the statuses your site really returns, from logs or a crawl export, with the ones your report names. Anything in the first list and not the second is a blind spot.

Registered check. index.broken.

See also: Pages an audit could not reach, A rate limit is not a block.

If a finding on your site looks wrong

Send us the address of the page and the text of the finding through the contact page. We read the finding against the page itself. If Docket was wrong, the fix gets a test like the ones above.

$349once · or 4 × $87.25

For Macs with Apple silicon or Intel, macOS 12 or later

Founding price $174.50 once · or 4 × $43.63, first 25 buyers, then $349. Buy applies the code FOUNDINGDOCKET for you; if the checkout shows $349, enter it under ‘Have a discount code?’.

Screaming Frog is $279 a year. Ahrefs plans with Site Audit run $29–$449 a month. Docket is $349 once. Their prices read 8 October 2026.

30 days, no conditions, no questions asked. Refund policy

Common questions

Has Docket ever reported something that was not true?

Yes. This page lists 10 releases that fixed findings or behaviour that were wrong, each with the day it was published and the tests that hold the fix.

Why would an audit report a problem my site does not have?

Usually because a check inferred something from text, a count, a tag or a name, and the inference failed on a site shaped differently from the ones it was written for. The 15 worked cases on this page are 15 such failures in Docket's own checks, each with what to check on your own report.

A finding's count and its headline disagree. Which is right?

Usually the headline, because a person wrote it and the badge came from a variable. Compare the count with the list of URLs under the finding before acting on it. One worked case here shows a count of the wrong thing lifting a minor finding above a more serious one.

What should I do if a Docket finding looks wrong on my site?

Send the address of the page and the finding to us through the contact page. We read the finding against the page, and if Docket was wrong the fix gets a test.