API for AI agents
Free, no key. JSON over HTTPS. Read only for AI agents: a meetup means people will show up at the place, so meetups and “I’m coming” taps come from people in the app.
Read
GET /api/volunteer/summary?city=dc|nyc|sf: meetups per place this week.{places:{id:{meetups,people,open_seats}},meetups,people,is_ai_counted:false,writes_open}GET /api/volunteer/place?city=dc&id=<id>: one place.{name,kind,meetups:[{id,host,type,type_label,hours,spots,bring,bring_label,starts,ends,seats_total,seats_left,seated:[{handle}]}],people,past:[{starts,ends,type_label,people_came,self_reported:true}],week_options}. Handles are random words, never names.
seats_total counts the host, so it is spots + 1. past lists meetups that ended in the last 2 days; people_came is the host plus everyone who tapped “I’m coming”. It is self-reported, nobody checks attendance (the app shows it as “N said they’d come”, not a head count), and AI agents are never counted.
Types come from a fixed list and must fit the place: park_cleanup (parks), beach_cleanup (beaches), garden_day (community gardens), litter_walk (parks, beaches), tree_care (parks, gardens), cards (cards for neighbors, at a library’s public tables). bring is a comma list from gloves, bags, grabber, water, sunscreen, trowel, rake, bucket, pens. Spots are 3, 5, 8, 10, 15 or 20.
Place ids, names and facts come from the static snapshot at /volunteer/data/<city>.json (© OpenStreetMap contributors, ODbL 1.0): public parks, beaches and community gardens, plus the public libraries from Language who else?. Private, residential, fee, military, campus, school and construction places are left out. Unnamed parks are named by their street ("named_by_street":true). hours is the raw OSM opening_hours string or null.
who else? is not affiliated with any park, garden, library, government agency or volunteer group. Meetups are informal.
Writes
POST /api/volunteer/post
content-type: application/json
{"city":"dc","id":"<id>","type":"park_cleanup","spots":8,"hours":2,
"bring":"gloves,bags","day":"2026-10-31","time":"10:00",
"client_id":"my-agent-0123456789","is_ai":true}
→ 403 {"code":"ai_read_only"}
- Every write with
"is_ai": truereturns 403ai_read_only. AI agents never count as people. - People post, tap “I’m coming”, say they can’t make it and cancel in the app, after confirming they are 18 or older.
dayis one of the next 7 days in the city’s time zone;timeis on the hour or half hour, 6am to 7pm, at least 15 minutes away;hoursis 1, 2 or 3. We send no messages, emails or notifications. - Allowed fields only: city, id, slot, type, hours, bring, spots, day, time, adult, device, client_id, is_ai. Any other field (name, note, phone…) or any value over 64 characters returns 422
no_free_textand nothing is stored. Files and non-JSON bodies return 415. - Other refusals: 422
bad_type(not on the list, or doesn’t fit the place),bad_spots,bad_bring,bad_hours,bad_time,place_closed(the place’s mapped hours don’t cover the whole meetup; unknown hours are allowed),not_daylight(parks, beaches and gardens: the whole meetup must fall between sunrise and sunset there, from SunCalc),adult_required; 409full,started,own_slot,already_seated,overlap,max_open(3 upcoming meetups per host); 429 when a daily limit is reached; 503volunteer_offwhen the kill switch is on (reads keep working,writes_open:false).
Limits
- Per device per day: 6 posts, 12 “I’m coming” taps, 20 can’t-make-its, 20 cancels; at most 3 upcoming meetups you host, and nothing that overlaps your other meetups. 80 writes per network per day, 5,000 writes per day for the app.
- Meetups leave the week list when they end and show as “N people came” for 2 days. Rows are deleted about 3 days after they end.
A who else? lab experiment. Place data © OpenStreetMap contributors, ODbL 1.0. Sunrise and sunset: SunCalc (BSD-2).