Methodology
Every source, window, radius, filter, formula and known limitation, stated plainly. Where this page names a file, the file is the authority and this page is the explanation.
Sources
- Traffic Crashes Resulting in Injury, DataSF dataset
ubvf-ztfx. Police-reported injury collisions, updated by the city on a rolling basis. StreetCred reads it live; nothing is copied into our store except computed counts. - 311 Cases, DataSF dataset
vw6y-z8j6. Resident service requests, updated continuously. - Street Intersections, DataSF dataset
gmfx-8h6i. One row per street leg; a real crossing is acnncarrying at least two distinct street names. 18,546 legs collapse to 8,254 crossings, which is the census the grades are measured against. 7,355 of them have reported harm and carry a published grade; 629 recorded no harm at all, and the remainder are one crossing counted twice where the city splits it into quadrants, whichtools/sweep.mjscollapses to the worst of them. That is why the masthead's count and this page's census are different numbers and both are right. - Street View imagery from Google, one photograph per corner, taken as the record of what the corner looks like, never as evidence of harm.
- Press coverage found by Exa search, filtered as described below. Resident accounts scraped ahead of time by Apify actors from public reviews and forums.
Two radii, on purpose
The displayed statistics count within 150 meters of the corner: the tiles, the letter's cited counts, the press context. The Danger Index grade counts within 80 meters, because a grade is a claim about one intersection, not about a block in every direction. Both surfaces say which radius they use, and the letter states both in the sentence that first cites a count. The two figures are measured over different areas and are not expected to reconcile; the silence about that difference was a bug this page is part of fixing, not the difference itself.
The 311 filter
Only these service categories count, verbatim, as an explicit allow list shared by every lane
(src/data.js):
Street Defects Street Defect Sign Repair Streetlights Sidewalk or Curb Sidewalk and Curb Blocked Street or SideWalk Blocked Street and Sidewalk Color Curb
The first version matched any category containing the word "Street", which swept in Street and Sidewalk Cleaning, a sanitation queue with millions of rows, and inflated one corner's count from 354 to 8,546. An allow list is uglier and correct; a substring match was elegant and wrong by a factor of 24.
The formula
collisionPoints = 10*fatal + 6*severe + 3*otherVisible + 1*pain + 2*pedInvolved maintenanceSignal = min(0.05 * safety311, 8) points = collisionPoints + maintenanceSignal
Collisions over five years, filtered 311 over twelve months, within 80 meters. The maintenance signal is capped at 8 points, less than one fatality, because a 311 report is paperwork and must never outweigh a person. An earlier uncapped version let 88 street-condition reports outvote a death; the cap is the correction, kept visibly small.
The census calibration, and its finality
A corner's index is a percentile: its position among all 8,254 real crossings
in the city, each scored with the formula above by tools/sweep.mjs on 2026-08-18
and frozen in src/distribution.js. The local counter behind the sweep was verified to match
the production queries exactly, corner by corner and field by field, before the array was written.
Shape of the city: 629 crossings with no recorded harm at all, median 3.1 points, 90th
percentile 29, maximum 196.9 (6th and Mission).
Grades: A below the 40th percentile, B to the 64th, C to the 79th, D to the 92nd, F at 93 and above. An F literally means more reported harm than 93 percent of San Francisco intersections. The index is capped at 99 because no corner is worse than itself. On the leaderboard the number beside each corner is the index itself: the grade is the percentile band, and the index is what separates corners inside one band, which is why a page of F corners still has an order.
This calibration is declared final. It replaced a 600-sample estimate that agreed with it to within a point or two (both medians 3.1). The array does not float with new data, because a grade that drifts with nothing changed on the ground is a grade nobody can cite, and people paste these into public comment. If a recalibration ever becomes genuinely unavoidable, it ships with a version bump, an entry on every affected corner's grade history, and a plain explanation here.
How the whole city is graded
Every intersection in San Francisco has a grade, and only some have an audit. Those are different claims and the site keeps them apart everywhere it makes them.
The sweep. tools/sweep.mjs pulls the two datasets in bulk once, buckets every row
into a 100 meter grid, and counts within 80 meters of each of the 8,254 crossings locally. That is
about a dozen paged requests instead of the roughly 25,000 API calls one query per corner would cost,
and the local counter was proved to reproduce production's own within_circle counts
exactly, corner by corner, before a single number was written down.
tools/sweep_districts.mjs then places each corner in a Supervisor district by the majority
of collision rows within 150 meters, using the same vote the live resolver uses.
The shards. tools/build_city_shards.mjs packs the 7,353 corners with recorded
harm into 71 KV bundles keyed by the first character of the slug, largest bundle 175 KB. One KV record
per corner would be 7,353 writes to publish the city and 7,353 more to correct it; a corner page is
instead one read of one bundle. The builder refuses to run if the committed census artifact and the
frozen array in src/distribution.js have drifted apart, because a grade measured against a
different yardstick than the one the code uses is worse than no grade.
The three tiers.
- AUDITED: every evidence lane has been checked here. Records, press, resident accounts and the visual audit have all run, and the corner has its two generated imagery states.
- ENRICHED: records and index checked and stored, no visual audit yet. These are the corners the daily cron works through next.
- SCORED: graded against the citywide census, no lane checked beyond the official record.
A SCORED grade uses the identical formula and census as an AUDITED grade; the difference is how many evidence lanes have been checked, never the math. A scored corner's Cred Check therefore lights official records and marks the other three lanes not yet checked, rather than counting them as failures: absence of a check is not a failed check.
The freshness rule. A SCORED corner's numbers are as of the sweep date, which is printed under its tiles, in its verdict sentence and on its source tag. ENRICHED and AUDITED corners show numbers pulled live at the moment you load them. Any corner promoted by the morning cron or resolved on demand switches to live numbers from that moment on. The provenance links under a swept number re-run the query live against data.sfgov.org today, which is why the caption states the as-of date in advance: a reader who clicks through and finds a slightly different count should already know why.
One more footprint note, because it is easy to trip over: a swept corner's tiles count within 80 meters over twelve months of 311, matching the grade's own footprint, where a live corner's tiles count within 150 meters over three years. Each tile says which it is.
Press checking at city scale, and what it costs
Running the per-page press search on every corner in the city would spend the balance on duplicates: every corner on Mission Street would run its own Mission Street search and get its own copy of the same corridor coverage. The batch lane is built around what is already paid for, and only moves to the next step when the previous one has nothing.
- The stored citywide sweep first. The watchlist has already read this month's coverage. A corner it already found articles for starts with candidates before a single call is planned.
- Then the segment cache. One stored entry per street, good for seven days, shared by every corner on that street. A corner whose streets are both warm costs no corridor search at all.
- Then three dated windows on the crossing itself, which is the only query that is genuinely corner specific: 2014 to 2019, 2020 to 2023, and 2024 to now. A search per year cost eleven calls to answer a question that reads the same at three.
Page text is bought last and only for what might be published. A search that asks for text pays for text on every result, including the ones the filter is about to discard, so these searches ask for none. Candidates are shortlisted on title and url, the shortlist alone is fetched, and only then is the corner-level bar applied to real text. The honest cost of that order: a story that names the corner only in its body, with a title and url that name neither street, can be missed by the shortlist. The lane errs toward showing less, which is the same direction every other filter here errs in.
Searched and empty is stored and shown as a result, not hidden. A corner with no coverage found says so, the same way the resident-voices funnel does.
A press-checked corner is not an audited corner. It keeps its tier and gains a press section. The imagery panel keeps its honest pending state, and the audited count on the homepage does not move because a corner was press checked.
How the map is drawn
The city map draws every graded crossing as a heat dot, with the three tiers marked: AUDITED filled, ENRICHED outlined, SCORED faint. Past zoom 15 the scored dots are tappable, through the same two-tap confirm as tapping the map itself. Unmarked crossings had no reported harm in the record, which is a statement about the record and not about the street.
The audited coverage layer is the 80m scoring core around each fully audited corner, drawn per corner and never as one outline: a boundary around the audited set would enclose thousands of crossings nobody has audited, and the gaps between discs are the truth of how much city remains. One more corner joins every morning.
The Danger Index ranks reported harm, not risk per crossing: no exposure normalization is applied, and the exposure caveat below states what that means in full.
How Exa is used
Five distinct modes, each with living proof:
- Per-corner press search: every warmed corner's press lane is a dated, cited search. See one live.
- Coverage timelines from 2014 onward, a search per window on the crossing itself. Same corner, the year strip.
- findSimilar corner connections, related coverage verified at both ends and written to both corners. A corner carrying one.
- The citywide watchlist, entity discovery over the city's coverage, with its rejects published beside what it kept. The watchlist.
- Standing radar monitors, watching for new coverage between runs. The radar.
Every Exa call is metered from the provider's own costDollars and logged in the public ledger.
The Press Watchlist, and how a corner gets on it
Every other lane on this site starts from a corner and asks what is written about it. The watchlist runs the other way: it starts from the city's coverage and asks which corners are in it. That is an entity-discovery problem, and it is only worth anything if the entities are verified.
29 citywide semantic searches are attempted each morning through Exa, with the news category, a published date window, lead-generation domains excluded at the API, and three passes restricted to San Francisco outlets that write at corner resolution. All 29 completed in the last pass. This lane has a cron trigger of its own so that it gets an invocation's subrequest budget to itself; it used to run as the last lane of the daily audit and inherited whatever that had left, which was about seven of fifty. Every crossing name in every result is pulled out by the same extractor the related-corner lead and the connections pass use, and then each candidate has to clear three bars before it can appear:
- Both names are San Francisco streets. Checked against the 2,219 street names in the graded city index. Page text is full of capitalized pairs joined by "and", and "Metro Areas and Our Cities" looks exactly like an intersection to a pattern match. Phrases that name no street are counted and discarded rather than listed, because a page of them teaches a reader nothing.
- The pair is an exact match in the intersections index. One KV read against the same 7,353 corner index the site grades from. A real pair of San Francisco streets that do not meet at a graded crossing is a reject worth reading, and it is published with that reason.
- The coverage is confirmed. The article has to be about safety at that crossing, not merely mention it. A redesign announcement listing six intersections in passing does not put six corners on a safety watchlist.
Rejects are logged and published at /watchlist with the reason each one failed. A discovery pipeline that shows only its hits is indistinguishable from a search box that got lucky.
This is an entity-discovery workflow of the shape Exa's Websets product is built for: find candidate entities, verify each against hard criteria, keep the ones that survive. It is implemented directly on Exa's search API, which is what the event credits cover.
Press connections use the same extractor and the same index. For an audited corner with a
best article, findSimilar asks what else is being written in the same breath; every
crossing named in the related coverage is verified the same way, and a surviving link is written to
both corners so the claim reads the same from either page. Two extra bars apply here, because
connecting two corners is a stronger claim than naming one: the connecting article must be dated (a
site homepage is not an article) and must be recent, since a blog post from 2007 is not the same breath
as anything. Empty stays empty, and nothing fuzzy is shown: of 23 audited corners, four have a
connection.
The exposure caveat, in full
The index ranks reported harm, not risk per crossing. A corner ten thousand people cross daily and a corner a hundred people cross daily are ranked on their raw counts. There is no reliable public pedestrian-volume dataset at intersection resolution for the whole city; rather than normalize against a guess, the index does not normalize at all and says so beside every score. Busy corners therefore rank high partly because they are busy. That is a real limitation, not a footnote.
What the visual audit is, and is not
A structured pass asks Gemini which of four fixed conditions it can actually see in the corner's
Street View frame: faded crosswalk markings, turning conflict, lighting, curb or sidewalk issues. It
returns booleans, not prose, and it can and does come back with nothing flagged. Everything after that
is arithmetic over city records (src/hazards.js):
- CONFIRMED: the model saw it and city records corroborate it. May be stated as documented.
- CANDIDATE: the model saw it, records do not yet show it. An observation, never a fact.
- REPORTED: records show it, the model did not see it. Belongs to the record, not the photograph.
The audit is advisory context. It never moves the grade.
The letter verification guarantee
Every letter is checked by src/verify.js before it is shown: every number against the
corner's own records, the addressee against the Supervisor table, every street named against the
intersection and its cited coverage, every cited domain against the sources actually fetched. A draft
that fails is rewritten once with the failing claim named; a draft that fails twice is not shown at all,
and the last verified letter serves instead, marked stale. The same verifier runs on letters submitted
by the autonomous watchdog, whose own claim about its work is recorded and then ignored in favour of
the arithmetic.
Known limitations, honestly
- Resident voices are sparse. Most corners have no scraped accounts, and the panel says so rather than borrowing quotes from elsewhere. Where accounts exist, most are about the neighborhood, not the street; the funnel counts are shown.
- Press coverage is often corridor-level. A story about Mission Street is not a story about one crossing. The press panel labels corridor coverage as such, and corner-level filtering errs toward showing less.
- Police-reported collisions undercount. Crashes that produce no report do not exist in
ubvf-ztfx, and reporting rates differ by neighborhood and by who was involved. - The 80m circle double-counts dense blocks. Adjacent crossings can share the same crashes; 6th Street's alley crossings each carry much of 6th Street's harm. That is inherent to counting by circle and it matches how the production queries count.
- Accessibility floor, not ceiling. The pages pass an automated audit with zero critical violations and support keyboard operation including the image slider; screen-reader flow beyond that has had less testing than sighted flow, and reports are welcome.
Corrections
Found a number this page cannot defend? Open an issue at github.com/alejandro-publius/streetcred with the corner and the receipt link beside the figure. Corrections ship with a grade-history entry when they move anything, and the public changelog keeps the record either way.
Grade changes: /changes · Service health: /status · The agent's decisions: /watchdog