Feed sightings from your own device using two endpoints. Your compute stays yours. Video never leaves your machine.
SparrowMap never accepts video. A contribution is a small, plate-illegible crop plus a position, and nothing else. Our side runs the classifier and a person confirms it before anything appears on the map. Only government and emergency vehicles are ever published; private vehicles are dropped.
So you send crops, we decide. You never send raw footage, and you never publish a claim directly.
Base URL: https://map.sparrowmap.com
POST /api/enrollSend a name and a rough starting position. For anything that moves (a dashcam, a car, a phone), set "kind": "mobile". A fixed camera can omit it.
curl -X POST https://map.sparrowmap.com/api/enroll \
-H "Content-Type: application/json" \
-d '{"name":"My Pi node","lat":XX.XXXX,"lon":-YY.YYYY,"kind":"mobile","contact":"your handle or email"}'
Response (the token is shown once — save it):
{ "id": "n_1a2b3c4d", "status": "active", "token": "KEEP-THIS-SECRET",
"review_url": "/rv", "review_token": "…" }
review_url + review_token let you review your own device's catches at /rv. Replace XX.XXXX / -YY.YYYY with your real latitude and longitude.
POST /api/sightingsSend the token in the Authorization header. Include a sub-resolution crop and where the vehicle sits in it. For a moving node, include the live GPS on every sighting.
curl -X POST https://map.sparrowmap.com/api/sightings \
-H "Authorization: Bearer KEEP-THIS-SECRET" \
-H "Content-Type: application/json" \
-d '{
"node_id": "n_1a2b3c4d",
"ts": 1700000000,
"lat": XX.XXXX, "lon": -YY.YYYY,
"source": "phone_node",
"snap_b64": "data:image/jpeg;base64,/9j/4AAQ…",
"vehicle_box": [x1, y1, x2, y2],
"body": "car"
}'
import base64, json, time, requests
with open("crop.jpg", "rb") as f:
b64 = "data:image/jpeg;base64," + base64.b64encode(f.read()).decode()
requests.post("https://map.sparrowmap.com/api/sightings",
headers={"Authorization": "Bearer KEEP-THIS-SECRET"},
json={
"node_id": "n_1a2b3c4d",
"ts": time.time(),
"lat": XX.XXXX, "lon": -YY.YYYY, # live GPS, per sighting
"source": "phone_node",
"snap_b64": b64,
"vehicle_box": [x1, y1, x2, y2],
"body": "car",
})
| Field | What it is | |
|---|---|---|
node_id | required | The id you got from /api/enroll. |
snap_b64 | required | A sub-resolution, plate-illegible crop as a data:image/jpeg;base64,… string. Keep it small (a couple hundred px of vehicle). Never full-res, never video. |
vehicle_box | required | [x1,y1,x2,y2] pixel box of the vehicle in the crop, so the server crops to the vehicle and not the street. |
lat, lon | required if mobile | The live GPS for this sighting. A fixed camera can omit these (it uses its watched span). A moving node must send them, or every dot pins to the enrol point. |
ts | optional | Unix time of the sighting. Defaults to now. |
source | optional | Use "phone_node" for a remote contributor: the crop is parked so our classifier scores it and a human reviews before publish. |
body | optional | Your guess at the vehicle type ("car", "truck"…). A hint only; the classifier decides. |
heading | optional | Direction the vehicle was travelling, degrees. |
plate_text / plate_conf | optional | Only if you read a plate. Weak reads are dropped; the crop still counts. |
503 "at capacity" means retry shortly, not an error in your code.We're building a scanner that finds surveillance cameras (Flock/ALPR) by the wifi they broadcast — no camera needed, just an Android phone or a Raspberry Pi. Same privacy rule: only surveillance devices leave your device, everything private is dropped at the edge.
Compute stays on your device, video never leaves it, and only public government vehicles are ever published. That is the whole point of SparrowMap.