LIVE NOW · VERIFIED PUBLIC CAMERA FEEDS
You built the map.
We have the feeds.
datum (n.) — the surveyed reference surface every other measurement is judged true against. That's the job: everyone else's camera list is a claim; this one is checked.
Live frames from ~8,500 public traffic cameras your page can actually render — CORS-clean, source bytes untouched, every camera health-polled and trust-scored so the dead ones are flagged before you draw them. Free key in one request.
Caltrans (SF to San Diego) · Washington State · Austin · Ottawa · Ontario 511 · TfL London · ~9,000 catalogued · /openapi.json · no signup to read the docs
// FIRST CALL · UNDER A MINUTE
Two commands. No signup flow.
Copy the block. It runs as-is — change the email, and step 2 picks the
key up automatically (needs jq; or paste the key by hand).
The GET also works with no key at all at 60 requests/hour,
so you can skip step 1 entirely if you just want a look.
total: 584 — what's left of Austin's 817 active cameras once the
filter keeps only the ones that pass every check; the other 233
on this run (2026-09-01 UTC — and 584 + 233 = 817, the arithmetic has
to close or one of the numbers is lying) were caught serving a
placeholder, same as below. Austin
throttles our polling, so that count moves cycle to cycle — see
Ottawa's 207-of-411 for the reproducible version of
this same finding. hasMore: true means it's still a slice, not
the set — page with &offset=, limit
maxes at 500. minTrust=N filters the same way, on
stats.trustScore instead.
Swap in a city name. ?city=austin,
?city=seattle, ?city=san-francisco,
?city=los-angeles, ?city=san-diego,
?city=sacramento, ?city=toronto,
?city=ottawa, ?city=london — case,
spaces and punctuation don't matter, and a city inside a
state-wide registry (Seattle inside Washington, SF inside
California) is narrowed to that city for you. A city we don't
cover gets a 400 that says so and names the ones we do —
never an empty list dressed up as an answer.
GET /api/registries lists every accepted name.
// DISCOVER COVERAGE FIRST
Ask what's there before you ask for it.
GET /api/registries is the whole fleet in one call — coverage,
licensing, and whether a browser can actually read the pixels — with zero
camera rows downloaded.
pixelReadable: true on TfL London and Caltrans California —
verified in a real headless browser, not asserted. A
<img src="{feedUrl}"> renders
any camera in this API. Reading actual pixels — fetch(),
<canvas>, deck.gl's BitmapLayer —
works cross-origin natively where pixelReadable is true (~4,000 cameras across those two registries) — and through GET /api/cameras/{id}/frame on every camera, no server of your own required.
// WHY A DIRECTORY AND NOT A LIST
“Active” is a claim. We check it.
A published camera list is a snapshot of intent. Ottawa's has no status field at all; Austin's says which of its cameras are switched on. Neither can tell you which cameras are returning actual pixels right now — and both are already out of date by the time you read them. So we fetch instead of trusting.
Five of the 207, run 2026-07-22 UTC against Ottawa's own feed URLs. Five HTTP 200s carrying one byte-identical, 22,379-byte JPEG — a bilingual “Camera Video Unavailable / Caméra vidéo non disponible” card, not a street.
- A status column is a promise. A fetch is evidence. Every
active camera in the directory is polled on a loop, and what comes
back is recorded:
uptime24h,uptime7d,lastOkAt. - Frozen frames are caught too.
freshcompares the image hash between polls — a camera stuck on a placeholder answers 200 forever but stops changing, and that shows up. - It rolls into one number.
trustScore(0–100) folds uptime, verification status and freshness together, so you can sort by "actually worth rendering" instead of by luck.nullmeans never polled — which is different from bad. - We keep the receipts, not just the verdict. Cameras that fail
verification come back as
verificationStatus: "contradicted"with a note saying what we caught, rather than quietly disappearing from the response. - And we're not immune to it either. Politeness costs us reach:
our poller deliberately backs off rather than hammer a source, so a
registry with an aggressive CDN costs us coverage on any single pass.
That's exactly why
trustScoreis a rolling measurement over many samples, not one check — and why the number above is Ottawa's, not a source that throttles us.
The same pattern shows up across the other registries, at different rates — we're not singling one city out. Austin's count moves with how much of its CDN we can reach in a given cycle, since we poll it politely rather than push through the throttling: 233 of 817 on the 2026-08-31 run, 273 and 338 on earlier ones — real, measured, just not a fixed number. The same day: Ontario 511 75 of 1,663, Washington State 166 of 1,533, Caltrans — the tightest catalog we ingest — just 9 of 3,304, and TfL London clean on every run we've measured. Fleet-wide that's 586 cameras marked active with no picture behind them — we re-fetch that count when this page loads; if the fetch fails, you're seeing the 2026-08-31 measurement.
// WHAT YOU GET
Five things the raw city feeds don't give you.
All of it travels inside the same camera object — nothing to join, nothing to look up in a second place.
01
Coordinates that survive a fetch
Each camera's coordinates are checked against the city's bounding box and de-duplicated — registries hand out one placeholder coordinate for whole batches — and its feed URL is fetched to confirm it returns an actual image.
That's auto_checked. Landmark-level confirmation
(verified) is a later pass we don't run yet, and we don't
pretend otherwise.
02
Uptime we measured, not uptime we were told
Every active camera is polled on a loop and every result is kept.
uptime24h, uptime7d, lastOkAt,
fresh and a 0–100 trustScore ride along with
every camera in the response.
A camera nobody has polled yet reports null, not zero. We
don't invent a number to fill the field.
One caveat we'd rather state than have you discover:
uptime measures whether the source answered our fetch. A
camera stuck on a placeholder card can hold a perfect
uptime24h: 1.0 — catching that is
verificationStatus and fresh's job, so read
them together, never uptime alone.
03
Licensing attached to every row
registry.licenseName, licenseUrl,
attribution and commercialOk travel with each
camera. Austin, Ontario 511 and TfL London publish clear terms —
commercialOk: true. Caltrans, WSDOT and Ottawa publish
none, so all three are commercialOk: null — and together
they are 5,248 of 8,507 active cameras (62%) as of 2026-08-31.
The majority of the fleet, not an edge case.
null means unknown, not cleared. We won't guess
on your behalf.
04
Docs written for the thing reading them
/llms.txt is the entire API as one plain-text file —
endpoints, field semantics, rate limits, and the gotchas that would
otherwise cost you an afternoon. /openapi.json for the
machine-readable spec. Neither needs a key.
Those gotchas aren't hypothetical — each one is written down because somebody hit it first.
05
Coverage before you fetch a single camera
GET /api/registries lists every source we ingest —
activeCameras/totalCameras, licensing,
tosNotes, and both proxyOk (legal) and
pixelReadable (technical, browser-verified) — with zero
camera rows downloaded.
Then narrow for real: minTrust or
verificationStatus on GET /api/cameras keep
only what's worth rendering — not everything a registry lists.
// WHAT'S LIVE, WHAT ISN'T
Cameras are deep. The rest is roadmap.
This is Phase 1. Everything under NOT BUILT YET is exactly that — not behind a flag, not in private beta, not "contact us". It does not exist yet, and nothing on this page pretends it does.
LIVE TODAY
- GET /api/camerasFilter by
city— a real city name (austin,seattle,san-francisco,toronto) or a whole registry region (california,washington,ontario) — pluscountry,bbox,minTrustorverificationStatus. Paginated:limit(max 500),offset,total,hasMore. - GET /api/cameras/{id}One camera, full record.
- GET /api/registriesCoverage, licensing and
pixelReadableper source — no camera download required. - GET /api/cameras/{id}/frameThe camera's current frame with
CORS solved —
fetch(),<canvas>and deck.gl'sBitmapLayerwork on every camera, not just thepixelReadableones. Bytes pass through from the source unmodified, cached per camera at the source's own polite cadence. - POST /api/keysFree key, issued instantly to any email. Shown once.
- GET /llms.txt · GET /openapi.jsonAgent docs and the OpenAPI spec. No key required.
- Rate limits, in the open60 req/h without a key, 3,600 with
one; frames have their own budget — 300 and 36,000.
X-RateLimit-*on every response,Retry-Afteron a 429. - Six registriesCaltrans California, Washington State, Austin, Ottawa, Ontario 511, TfL London — ~9,000 cameras listed, ~8,500 active, health-polled continuously.
NOT BUILT YET
- /streamA live MJPEG stream per camera. Until it exists,
poll
/frameat the camera'sminPollIntervalS— polling faster just re-reads the same cached frame. - Historical framesNothing is archived yet. There is no back-in-time endpoint to call.
- Aircraft (ADS-B) · satellites (TLE)Planned as passthrough behind the same key. Not in the API today.
- More registriesThe adapter shape is proven on six. Which comes next is the question below.
NOTED
We'll email when the things you picked ship.
Your picks go straight into the build order. Meanwhile the cameras are live — grab a key and make the first call.
Public data only. Free tier stays free.