Small car-specs site, generated pages, one per diagnostic trouble code. The build script decides what goes in. Nobody reads the output.
So I read it. All 76 pages, fetched like a stranger would, and the thing that fell out is not a coverage gap — it is that P0303 and P0304 are P0302 with the digit swapped. 99.4% character similarity on the body text. Eight difference blocks. Every block is the numeral.
That is what happens when a template owns the writing and a for loop owns the variation.
How we measured
Pulled sitemap-obd.xml, 76 URLs. Fetched with Python 3.13, urllib, single thread, 300ms apart, three attempts each before declaring a failure. All 76 returned 200.
Per page: strip script, style, nav, footer; count words; regex the labelled fields out of the HTML (system, standard, severity, symptom count, cause count, diagnosis length, sibling list).
For the duplicate check I used two measures on purpose. difflib character similarity on the body text between the system label and the sibling list — that boundary matters, because the sibling list changes on every page and would drag the score down for a reason that is not duplication. And Jaccard on 6-word shingles as a cross-check.
For the record: my first pass compared whole pages and reported 60–83% similarity. That number was contaminated by the sibling lists. Restricted to the body, the true figure for P0303 is 99.4%.
What is in there
| Prefix | Codes | What the prefix covers |
|---|---|---|
| P | 70 | Powertrain |
| U | 6 | Network / communication |
| B | 0 | Body (airbags, restraints, comfort) |
| C | 0 | Chassis (ABS, steering, suspension) |
| System | Codes | Number range |
|---|---|---|
| Fuel & Air Metering | 33 | P0100–P0299 |
| Ignition & Misfire | 12 | P0300–P0352 |
| Emissions Control | 9 | P0401–P0456 |
| Transmission | 8 | P0700–P0755 |
| Network | 6 | U0001–U0155 |
| Electrical | 5 | P0562–P0606 |
| Speed & Idle Control | 3 | P0500–P0507 |
| Severity label | Codes |
|---|---|
| Repair soon | 45 |
| Low urgency | 22 |
| Do not keep driving | 9 |
| Measure | Value |
|---|---|
| Words per page, min | 382 |
| Median | 426 |
| Max | 478 |
| Standard deviation | 17.1 |
| Pages listing every peer in their own system | 14 of 76 |
| Pages listing exactly 6 peers | 62 of 76 |
Three things I did not expect
The misfire run stops at cylinder six.
P0301 through P0306 exist. All six carry the "Do not keep driving" label, which is the right call — an unburnt-fuel misfire will cook a catalytic converter.
P0307 and P0308 return 404. I retried both twice.
So we tell the owner of a V6 that a misfire means stop driving, and we tell the owner of a V8 nothing at all. The most common vehicles on American roads are V8 trucks. The coverage stops exactly where the biggest engines begin.
Two of those pages are literally the same page with the digit changed.
I compared the body text only — everything between the system label and the sibling list — so the "other codes" block could not inflate the score.
P0303 against P0302: 99.4% character similarity. Eight difference blocks. Every one of the eight is the numeral 2 replaced by a 3. Nothing else.
P0304 against P0302: 99.4%. Eight blocks. Every one is 2 replaced by 4.
That is not two pages about two different faults. That is one page, twice, with the cylinder number stamped in. Same cause list, same diagnosis paragraph, same FAQ. Google compares text, not intent.
P0305 and P0306 are the honest ones, and the contrast is the useful part. 81% similarity, not 99%. The sentences were actually rewritten — different cause ordering, different phrasing in the diagnosis, plus one detail worth keeping: cylinder numbering differs between V6 and V8 engines, so confirm which cylinder you are actually chasing. Whoever wrote those two did the work. Whoever generated 3 and 4 did not.
Navigation gives up at six, no matter how many peers exist.
62 of the 76 pages render exactly six "other codes in this system" links. Fuel & Air holds 33 codes and shows six of them. Ignition holds 12 and shows six. Six is a slice(0, 6) in the template, not a decision about how many neighbours a reader can use — and nothing on the page says the list is partial.
Six is not a design decision about how many links a reader wants. It is a slice(0, 6). The result: 62 pages cannot reach two-thirds of their own neighbours, and no page in the site tells you which six you are missing.
And every page is the same length. 382 to 478 words, standard deviation 17, whether the page sits in a system with 3 codes or 33. The template decides the length, not the fault.
One thing we wrote wrong
Every one of the 76 pages carries the site footer: "All figures sourced from NHTSA and the U.S. Department of Energy (fueleconomy.gov)."
That sentence is true for vehicle specs. It is false on every OBD page. Code numbers, system names and definitions on these pages come from the SAE J2012 / ISO 15031-6 generic code standard — which the sourcing box on the same page says, three inches above the footer. Two boxes, one page, contradicting each other, 76 times.
We are fixing the footer template this week. The OBD pages are not NHTSA data and should never have said so.
What this measurement cannot tell you
Which of the missing codes anyone actually looks up.
I have no keyword data for this site and I did not buy any for a blog post. There are 167 empty numbering slots in the Fuel & Air block and most of them probably correspond to nothing anyone has ever typed into a search box. The honest statement is: P0307 and P0308 are gaps I can prove matter, because a reader asked about one. The other 160-odd gaps I can only count, not rank.
I also did not test any of this against a live vehicle. The symptom and cause lists are written from the generic definition, not from a scan of a real car. If your P0171 turned out to be a vacuum leak and our page said intake gasket, that is the gap between a generic code and your specific engine, and no amount of page-building closes it.
Disclosure
I build CarSpecData, which is the site doing the publishing here. Every number above came out of my own pages and my own templates. The duplicate pair is mine, the footer error is mine, and the slice(0, 6) is mine.
If you own a V8 and you are chasing a misfire right now, we do not have your page yet. That is the state of it. The template fix goes in first; the missing cylinders go in after.


