Barrelman Docs

Search

Fuzzy place search over the OSM extract, plus spatial relationship queries — which areas contain a point, and the children of an area.

Search places

Unified search endpoint supporting text search, category browsing, and route corridor search.

Text search (provide query): Runs a hybrid four-layer pipeline:

  1. Full-text search (FTS) via tsvector GIN index
  2. Trigram fuzzy match via pg_trgm GIN index
  3. Abbreviation match on pre-computed name_abbrev
  4. Semantic vector search via pgvector (conditional)

Results are deduplicated in priority order (FTS > abbreviation > trigram > semantic) then re-ranked with proximity decay when coordinates are provided.

Browse mode (omit query, provide categories and/or tags): Returns matching places sorted by distance. Requires a spatial constraint (lat/lng or route).

Spatial modes:

  • Point: lat + lng + optional radius — search within a circular area
  • Route corridor: route (GeoJSON LineString) + optional buffer — search along a path with exponential proximity decay

Both modes can be combined with text search and/or category/tag filters.

POST
/search

Request Body

application/jsonRequired
querystring

Search query text. When omitted, returns places matching categories/tags sorted by proximity (browse mode).

latnumber

Latitude for point-based spatial filtering and proximity ranking (WGS 84).

lngnumber

Longitude for point-based spatial filtering and proximity ranking (WGS 84).

radiusnumber

Search radius in meters around the lat/lng point. Max recommended: 50000.

routeobject

GeoJSON LineString geometry. When provided, search is constrained to a corridor around this route instead of a point radius.

buffernumber

Corridor width in meters when using route mode. Places closer to the route are strongly preferred via exponential decay ranking.

Default: 1000
categoriesarray<string>

OSM preset category IDs to filter by (e.g. ["fuel", "cafe"]). Multiple values are OR'd together.

tagsobject

Additional OSM tag key/value pairs that must all be present (JSONB containment).

limitnumber

Maximum number of results to return.

Default: 20
offsetnumber

Number of results to skip for pagination (browse mode only).

Default: 0
semanticboolean

Force semantic vector search for concept queries (e.g. "somewhere quiet to study"). Requires Ollama.

Default: false
autocompleteboolean

Enable autocomplete mode — skips the slow semantic layer for low-latency typeahead.

Default: false

Response Body

curl -X POST "https://example.com/search" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "coffee",
    "lat": 35.2271,
    "lng": -80.8431,
    "radius": 1000,
    "route": {
      "type": "LineString",
      "coordinates": [
        [
          0,
          0
        ]
      ]
    },
    "buffer": 500,
    "categories": [
      "fuel"
    ],
    "tags": {
      "cuisine": "pizza"
    },
    "limit": 10,
    "offset": 0,
    "semantic": false,
    "autocomplete": false
  }'
const body = JSON.stringify({
  "query": "coffee",
  "lat": 35.2271,
  "lng": -80.8431,
  "radius": 1000,
  "route": {
    "type": "LineString",
    "coordinates": [
      [
        0,
        0
      ]
    ]
  },
  "buffer": 500,
  "categories": [
    "fuel"
  ],
  "tags": {
    "cuisine": "pizza"
  },
  "limit": 10,
  "offset": 0,
  "semantic": false,
  "autocomplete": false
})

fetch("https://example.com/search", {
  body
})
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "strings"
)

func main() {
  url := "https://example.com/search"
  body := strings.NewReader(`{
    "query": "coffee",
    "lat": 35.2271,
    "lng": -80.8431,
    "radius": 1000,
    "route": {
      "type": "LineString",
      "coordinates": [
        [
          0,
          0
        ]
      ]
    },
    "buffer": 500,
    "categories": [
      "fuel"
    ],
    "tags": {
      "cuisine": "pizza"
    },
    "limit": 10,
    "offset": 0,
    "semantic": false,
    "autocomplete": false
  }`)
  req, _ := http.NewRequest("POST", url, body)
  req.Header.Add("Content-Type", "application/json")
  res, _ := http.DefaultClient.Do(req)
  defer res.Body.Close()
  body, _ := ioutil.ReadAll(res.Body)

  fmt.Println(res)
  fmt.Println(string(body))
}
import requests

url = "https://example.com/search"
body = {
  "query": "coffee",
  "lat": 35.2271,
  "lng": -80.8431,
  "radius": 1000,
  "route": {
    "type": "LineString",
    "coordinates": [
      [
        0,
        0
      ]
    ]
  },
  "buffer": 500,
  "categories": [
    "fuel"
  ],
  "tags": {
    "cuisine": "pizza"
  },
  "limit": 10,
  "offset": 0,
  "semantic": false,
  "autocomplete": false
}
response = requests.request("POST", url, json = body, headers = {
  "Content-Type": "application/json"
})

print(response.text)
Empty

Find areas containing a point

Returns all named area geometries whose polygons contain the given coordinate point. Results are ordered smallest-first (innermost area first), so the most specific containing region appears at index 0.

Typical use: reverse-geocode a coordinate to its administrative hierarchy (building → neighbourhood → city → county → state → country) or find which venue/campus a point is inside.

Only areas with names are returned. building:part features are excluded. Both centroid and full GeoJSON geometry are included in the response.

GET
/contains

Query Parameters

latRequiredstring

Latitude of the point to test containment for (WGS 84).

lngRequiredstring

Longitude of the point to test containment for (WGS 84).

excludestring

Barrelman place ID to exclude from results. Useful to omit the place you are currently viewing (e.g. the place whose detail page you are on).

Response Body

curl -X GET "https://example.com/contains?lat=string&lng=string&exclude=string"
fetch("https://example.com/contains?lat=string&lng=string&exclude=string")
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {
  url := "https://example.com/contains?lat=string&lng=string&exclude=string"

  req, _ := http.NewRequest("GET", url, nil)
  
  res, _ := http.DefaultClient.Do(req)
  defer res.Body.Close()
  body, _ := ioutil.ReadAll(res.Body)

  fmt.Println(res)
  fmt.Println(string(body))
}
import requests

url = "https://example.com/contains?lat=string&lng=string&exclude=string"

response = requests.request("GET", url)

print(response.text)
Empty

Find children of an area

Returns places whose centroids fall within the geometry of the given parent area (e.g. all shops inside a mall, all POIs inside a university campus).

Sorting priority:

  1. Named places first (unnamed amenities ranked lower)
  2. Places with recognized categories before unclassified features
  3. Proximity to lat/lng if provided, otherwise proximity to the parent area's centroid
  4. Alphabetical by name as a tiebreaker

Category filtering applies only to unnamed children — named places inside the area are always returned, allowing landmark buildings and venues to appear even when filtering for a specific type.

building:part features are always excluded to avoid surfacing architectural sub-elements.

GET
/children

Query Parameters

idRequiredstring

Barrelman place ID of the parent area (e.g. way/123456). Must be an area geometry (geom_type = 'area').

categoriesstring

Comma-separated list of OSM preset category IDs to filter unnamed children by. Named places inside the area are always included regardless of this filter.

limitstring

Maximum number of children to return.

offsetstring

Number of children to skip for pagination.

latstring

Latitude for proximity sorting. When provided, results closer to this point are ranked first. Falls back to parent centroid when omitted.

lngstring

Longitude for proximity sorting. When provided, results closer to this point are ranked first. Falls back to parent centroid when omitted.

Response Body

curl -X GET "https://example.com/children?id=string&categories=string&limit=string&offset=string&lat=string&lng=string"
fetch("https://example.com/children?id=string&categories=string&limit=string&offset=string&lat=string&lng=string")
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {
  url := "https://example.com/children?id=string&categories=string&limit=string&offset=string&lat=string&lng=string"

  req, _ := http.NewRequest("GET", url, nil)
  
  res, _ := http.DefaultClient.Do(req)
  defer res.Body.Close()
  body, _ := ioutil.ReadAll(res.Body)

  fmt.Println(res)
  fmt.Println(string(body))
}
import requests

url = "https://example.com/children?id=string&categories=string&limit=string&offset=string&lat=string&lng=string"

response = requests.request("GET", url)

print(response.text)
Empty