Skip to main content

Ingest places

Status: Proposed 2026-09-29, farmers markets added to the OSM step 2026-10-24

Three scripts feed places: ingest:places:osm, ingest:places:gardens, and ingest:sanctuaries. All three POST to POST /api/ingest/places under the contract. Places are the safest resource to automate because a place is a business or a field, never a person. The first import and its history are on the data sources page.

Item shape​

FieldRequiredFrom
sourceIdyesosmId (node/123, way/456, relation/789) or the curated key
name, type, veganLevelyessee the mapping below
location { lng, lat }yesthe node itself, center for ways and relations, or geocoded
address, city, postcodeaddr:housenumber plus addr:street, addr:city, addr:postcode
website, phone, hourswebsite or contact:website, phone or contact:phone, opening_hours
tags[]see richer tags
descriptionsource text, else generated
chaintrue when brand or brand:wikidata is set
sourceUrlhttps://www.openstreetmap.org/ followed by the osmId

area is not sent. The API derives it from coordinates through a table of bounding boxes per home area, and city falls back to that box's name when addr:city is missing. A free-text city never decides the area.

OpenStreetMap​

Query the Overpass mirror inside the Southern California bounding box with out center tags, sending a descriptive User-Agent. Two clauses share that tiled fetch: nwr["diet:vegan"~"^(yes|only)$"], and named nwr["amenity"="marketplace"]. The same mirror fallback and the same stop after consecutive failed tiles cover both.

OSM tagstypeveganLevel
diet:vegan=onlyby amenity or shopfull
diet:vegan=yesby amenity or shopoptions
amenity=restaurant, amenity=food_courtrestaurant
amenity=fast_food with diet:vegan=onlyrestaurantfull
amenity=fast_food with diet:vegan=yesskipped
amenity=cafe, amenity=ice_creamcafe
amenity=bar, amenity=pubvenue
shop=supermarket, greengrocer, health_food, conveniencegrocery
named farmers or produce amenity=marketplacegrocery, tag market:farmers, chain falseoptions, or full when diet:vegan=only
any other shop=*shop
anything else, including flea, craft, and antique marketsskipped and counted

A marketplace counts as farmers or produce when marketplace or marketplace:type is a farmers or produce value, or when the name reads as a farmers market, produce market, greenmarket, or farm stand. A cafe that is also tagged diet:vegan stays a cafe: the diet mapping runs when the feature is not a farmers market. The schedule is the nightly OSM step on the automations page, 12:42 AM Pacific. Farmers markets are inside that step. Six tiles in a row failing on both Overpass mirrors abort the run; the next night retries.

The fast-food rule in one line: a fast-food place with a vegan option is not worth a pin, a fully vegan one is.

Chains: brand or brand:wikidata present sets chain: true, except on a farmers market, where chain stays false. The public API hides chains unless the caller asks for them (includeChains=true) and never deletes them, because a fully vegan chain is still useful.

Richer tags go into tags[] as key:value strings, so the client can filter without a schema change: cuisine:thai (one tag per value of a semicolon-separated cuisine), wheelchair:yes, outdoor_seating:yes, takeaway:yes, delivery:yes. Only the values yes, no, and limited are kept for the last four.

Description: when OSM has no description, generate one sentence from what it does have, level first, then type, city, cuisine: "Fully vegan cafe in Long Beach. Cuisine: thai, vegan." Never invent hours or claims.

Community gardens​

ingest:places:gardens queries leisure=garden with garden:type=community, plus landuse=allotments that carry a name. Every hit becomes type: garden, veganLevel: full, with website and opening_hours when present and a generated description ("Community garden in San Pedro."). Unnamed allotments are skipped: a pin without a name helps nobody. Gardens matter because growing food is the quietest form of outreach a Grove can host.

Curated sanctuaries​

ingest:sanctuaries reads scripts/data/sanctuaries.json in the API repo. Each entry is hand-verified from the sanctuary's own site: name, address, website, a description in the app's own words, tags such as tours and volunteering. It is sent with source: curated, a stable kebab-case sourceId (farm-sanctuary-socal), type: sanctuary, veganLevel: full.

Entries without coordinates are geocoded with Nominatim at no more than one request per second with a descriptive User-Agent, and the result is written back into the JSON so the lookup runs once. Adding a sanctuary is a pull request to that file with the source page linked in the PR.

Cadence and attribution​

ScriptCadenceNotes
ingest:places:osmnightly, 12:42 AM Pacific--dry-run, read the counts, then run. Includes farmers and produce markets. An Overpass outage (six tiles in a row failing on both mirrors) aborts the run for the next night
ingest:places:gardensnightlysame run as OSM
ingest:sanctuarieswhen the JSON changessmall, safe to run any time

OpenStreetMap data is ODbL. A place with source: osm shows "Data from OpenStreetMap contributors" on its page, and the map tiles carry the OSM attribution in the style. Nominatim results are also ODbL and are used only to place a curated pin.