diff --git a/Makefile b/Makefile
index b5ede9eb..b5fb6bb8 100644
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,7 @@ LDFLAGS=-s -w
 cli:
 	go build -ldflags="$(LDFLAGS)" -mod $(GOMOD) -o bin/http-server cmd/http-server/main.go
 	go build -ldflags="$(LDFLAGS)" -mod $(GOMOD) -o bin/grpc-server cmd/grpc-server/main.go
+	go build -ldflags="$(LDFLAGS)" -mod $(GOMOD) -o bin/grpc-client cmd/grpc-client/main.go
 	go build -ldflags="$(LDFLAGS)" -mod $(GOMOD) -o bin/update-hierarchies cmd/update-hierarchies/main.go
 	go build -ldflags="$(LDFLAGS)" -mod $(GOMOD) -o bin/pip cmd/pip/main.go
 
@@ -14,3 +15,7 @@ httpd:
 	go run cmd/http-server/main.go \
 		-enable-www \
 		-spatial-database-uri "sqlite://?dsn=$(DSN)"
+
+grpcd:
+	go run cmd/grpc-server/main.go \
+		'sqlite://?dsn=$(DSN)'
diff --git a/README.md b/README.md
index 8ba3d4b6..f24ce0a8 100644
--- a/README.md
+++ b/README.md
@@ -26,9 +26,7 @@ Here's an example of the creating a compatible SQLite database for all the [admi
 ```
 $> ./bin/wof-sqlite-index-features \
 	-index-alt-files \
-	-rtree \
-	-spr \
-	-properties \
+	-spatial-tables \
 	-timings \
 	-dsn /usr/local/ca-alt.db \
 	-mode repo:// \
@@ -381,36 +379,10 @@ $> go run -mod vendor cmd/query/main.go \
 
 _Big thanks to @psanford 's [sqlitevfshttp](https://github.com/psanford/sqlite3vfshttp) package for making this possible._
 
-## Interfaces
-
-This package implements the following [go-whosonfirst-spatial](#) interfaces.
-
-### spatial.SpatialDatabase
-
-```
-import (
-	"github.com/whosonfirst/go-whosonfirst-spatial/database"
-	_ "github.com/whosonfirst/go-whosonfirst-spatial-sqlite"       
-)
-
-db, err := database.NewSpatialDatabase(ctx, "sqlite://?dsn={DSN}")
-```
-
-### spatial.PropertiesReader
-
-```
-import (
-	"github.com/whosonfirst/go-whosonfirst-spatial/properties"
-	_ "github.com/whosonfirst/go-whosonfirst-spatial-sqlite"       
-)
+### http-server
 
-pr, err := properties.NewPropertiesReader(ctx, "sqlite://?dsn={DSN}")
 ```
-
-## server
-
-```
-> ./bin/server -h
+> ./bin/http-server -h
   -authenticator-uri string
     	A valid sfomuseum/go-http-auth URI. (default "null://")
   -cors-allow-credentials
@@ -466,7 +438,7 @@ pr, err := properties.NewPropertiesReader(ctx, "sqlite://?dsn={DSN}")
 For example:
 
 ```
-$> bin/server \
+$> bin/http-server \
 	-enable-www \
 	-spatial-database-uri 'sqlite:///?dsn=modernc:///usr/local/data/sfomuseum-data-architecture.db'
 ```
@@ -482,7 +454,7 @@ When you visit `http://localhost:8080` in your web browser you should see someth
 If you don't need, or want, to expose a user-facing interface simply remove the `-enable-www` and `-nextzen-apikey` flags. For example:
 
 ```
-$> bin/server \
+$> bin/http-server \
 	-spatial-database-uri 'sqlite:///?dsn=modernc:///usr/local/data/sfomuseum-data-architecture.db' 
 ```
 
@@ -527,7 +499,7 @@ By default, results are returned as a list of ["standard places response"](https
 
 
 ```
-$> bin/server \
+$> bin/http-server \
 	-enable-geojson \
 	-spatial-database-uri 'sqlite:///?dsn=modernc:///usr/local/data/sfomuseum-data-architecture.db'
 ```
@@ -578,10 +550,97 @@ $> curl -s -XPOST -H 'Accept: application/geo+json' 'http://localhost:8080/api/p
 }  
 ```
 
+### grpc-server
+
+```
+$> ./bin/grpc-server -h
+  -custom-placetypes string
+    	A JSON-encoded string containing custom placetypes defined using the syntax described in the whosonfirst/go-whosonfirst-placetypes repository.
+  -enable-custom-placetypes
+    	Enable wof:placetype values that are not explicitly defined in the whosonfirst/go-whosonfirst-placetypes repository.
+  -host string
+    	The host to listen for requests on (default "localhost")
+  -is-wof
+    	Input data is WOF-flavoured GeoJSON. (Pass a value of '0' or 'false' if you need to index non-WOF documents. (default true)
+  -iterator-uri string
+    	A valid whosonfirst/go-whosonfirst-iterate/v2 URI. Supported schemes are: directory://, featurecollection://, file://, filelist://, geojsonl://, null://, repo://. (default "repo://")
+  -port int
+    	The port to listen for requests on (default 8082)
+  -properties-reader-uri string
+    	A valid whosonfirst/go-reader.Reader URI. Available options are: [fs:// null:// repo:// sqlite:// stdin://]. If the value is {spatial-database-uri} then the value of the '-spatial-database-uri' implements the reader.Reader interface and will be used.
+  -spatial-database-uri string
+    	A valid whosonfirst/go-whosonfirst-spatial/data.SpatialDatabase URI. options are: [rtree:// sqlite://] (default "rtree://")
+```	
+
+For example:
+
+```
+$> ./bin/grpc-server -spatial-database-uri 'sqlite://?dsn=modernc:///usr/local/data/arch.db' 
+2024/07/19 10:52:47 Listening on localhost:8082
+```
+
+And then in another terminal:
+
+```
+$> ./bin/grpc-client -latitude 37.621131 -longitude -122.384292 | jq '.places[]["name"]'
+"San Francisco International Airport"
+```
+
+### grpc-client
+
+```
+$> ./bin/grpc-client -h
+  -alternate-geometry value
+    	One or more alternate geometry labels (wof:alt_label) values to filter results by.
+  -cessation string
+    	A valid EDTF date string.
+  -geometries string
+    	Valid options are: all, alt, default. (default "all")
+  -host string
+    	The host of the gRPC server to connect to. (default "localhost")
+  -inception string
+    	A valid EDTF date string.
+  -is-ceased value
+    	One or more existential flags (-1, 0, 1) to filter results by.
+  -is-current value
+    	One or more existential flags (-1, 0, 1) to filter results by.
+  -is-deprecated value
+    	One or more existential flags (-1, 0, 1) to filter results by.
+  -is-superseded value
+    	One or more existential flags (-1, 0, 1) to filter results by.
+  -is-superseding value
+    	One or more existential flags (-1, 0, 1) to filter results by.
+  -latitude float
+    	A valid latitude.
+  -longitude float
+    	A valid longitude.
+  -null
+    	Emit results to /dev/null
+  -placetype value
+    	One or more place types to filter results by.
+  -port int
+    	The port of the gRPC server to connect to. (default 8082)
+  -property value
+    	One or more Who's On First properties to append to each result.
+  -sort-uri value
+    	Zero or more whosonfirst/go-whosonfirst-spr/sort URIs.
+  -stdout
+    	Emit results to STDOUT (default true)
+```
+
+For example:
+
+```
+$> ./bin/grpc-client -latitude 37.621131 -longitude -122.384292 | jq '.places[]["name"]'
+"San Francisco International Airport"
+```
+
 ## See also
 
-* https://www.sqlite.org/rtree.html
 * https://github.com/whosonfirst/go-whosonfirst-spatial
+* https://github.com/whosonfirst/go-whosonfirst-spatial-www
+* https://github.com/whosonfirst/go-whosonfirst-spatial-grpc
 * https://github.com/whosonfirst/go-whosonfirst-sqlite
 * https://github.com/whosonfirst/go-whosonfirst-sqlite-features
+* https://github.com/whosonfirst/go-whosonfirst-sqlite-features-index
 * https://github.com/whosonfirst/go-reader
diff --git a/cmd/grpc-client/main.go b/cmd/grpc-client/main.go
new file mode 100644
index 00000000..87218073
--- /dev/null
+++ b/cmd/grpc-client/main.go
@@ -0,0 +1,20 @@
+package main
+
+import (
+	"context"
+	"log"
+
+	"github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/client"	
+)
+
+func main() {
+
+	ctx := context.Background()
+	logger := log.Default()
+
+	err := client.Run(ctx, logger)
+
+	if err != nil {
+		logger.Fatalf("Failed to run client, %v", err)
+	}
+}
diff --git a/go.mod b/go.mod
index e558d87b..0d50e510 100644
--- a/go.mod
+++ b/go.mod
@@ -11,7 +11,7 @@ require (
 	github.com/whosonfirst/go-ioutil v1.0.2
 	github.com/whosonfirst/go-reader v1.0.2
 	github.com/whosonfirst/go-whosonfirst-spatial v0.10.0
-	github.com/whosonfirst/go-whosonfirst-spatial-grpc v0.1.1
+	github.com/whosonfirst/go-whosonfirst-spatial-grpc v0.1.2
 	github.com/whosonfirst/go-whosonfirst-spatial-www v0.2.0
 	github.com/whosonfirst/go-whosonfirst-spr/v2 v2.3.7
 	github.com/whosonfirst/go-whosonfirst-sqlite-features/v2 v2.0.3
diff --git a/go.sum b/go.sum
index a1ef8d45..9522f0cf 100644
--- a/go.sum
+++ b/go.sum
@@ -341,8 +341,8 @@ github.com/whosonfirst/go-whosonfirst-sources v0.1.0 h1:JuKLa6KWke22jBfJ1pM9WQHo
 github.com/whosonfirst/go-whosonfirst-sources v0.1.0/go.mod h1:EUMHyGzUmqPPxlMmOp+28BFeoBdxxE0HCKRd67lkqGM=
 github.com/whosonfirst/go-whosonfirst-spatial v0.10.0 h1:l6OwMOy5HazP8vf0EDmgjIGXPPofIX6FHpY/JOX24NE=
 github.com/whosonfirst/go-whosonfirst-spatial v0.10.0/go.mod h1:ZYUHliaEMtm3R43ZBTgdRVGhnF92LbrBehUSmR2T7go=
-github.com/whosonfirst/go-whosonfirst-spatial-grpc v0.1.1 h1:20MqWjgiWCHIyAhT28PEgMqoBrhwC6lZ4RPdv4DXo1w=
-github.com/whosonfirst/go-whosonfirst-spatial-grpc v0.1.1/go.mod h1:giIqrHTO6grd1Geu3gBa2EY2D+GoVSZAY2l9oJYbtRk=
+github.com/whosonfirst/go-whosonfirst-spatial-grpc v0.1.2 h1:0a+lES3zv/1iz0ppqMiuLIMxHW+IxO4VTwV7yIo+mw0=
+github.com/whosonfirst/go-whosonfirst-spatial-grpc v0.1.2/go.mod h1:giIqrHTO6grd1Geu3gBa2EY2D+GoVSZAY2l9oJYbtRk=
 github.com/whosonfirst/go-whosonfirst-spatial-www v0.2.0 h1:LpjuvmBznHQnYbYquo0YPYSJy0dab2sBRP5t6I5A9HU=
 github.com/whosonfirst/go-whosonfirst-spatial-www v0.2.0/go.mod h1:yoPKlKjXg0fdRnMuZhcuurn2mBKnN3dMli/4w8TYGas=
 github.com/whosonfirst/go-whosonfirst-spr-geojson v0.0.8 h1:K0rCaKo0xvOtuS/x9r62bEfIK3OTdZqbDmgG+A3xDSs=
diff --git a/vendor/github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/client/client.go b/vendor/github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/client/client.go
new file mode 100644
index 00000000..64e0d460
--- /dev/null
+++ b/vendor/github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/client/client.go
@@ -0,0 +1,106 @@
+package client
+
+import (
+	"context"
+	"encoding/json"
+	"flag"
+	"fmt"
+
+	"github.com/whosonfirst/go-whosonfirst-spatial-grpc/request"
+	"github.com/whosonfirst/go-whosonfirst-spatial-grpc/spatial"
+	"github.com/whosonfirst/go-whosonfirst-spatial/pip"
+	"google.golang.org/grpc"
+	"io"
+	"log"
+	"os"
+)
+
+func Run(ctx context.Context, logger *log.Logger) error {
+
+	fs, err := DefaultFlagSet()
+
+	if err != nil {
+		return fmt.Errorf("Failed to create default flagset, %w", err)
+	}
+
+	return RunWithFlagSet(ctx, fs, logger)
+}
+
+func RunWithFlagSet(ctx context.Context, fs *flag.FlagSet, logger *log.Logger) error {
+
+	opts, err := RunOptionsFromFlagSet(ctx, fs)
+
+	if err != nil {
+		return fmt.Errorf("Failed to derive options from flagset, %w", err)
+	}
+
+	return RunWithOptions(ctx, opts, logger)
+}
+
+func RunWithOptions(ctx context.Context, opts *RunOptions, logger *log.Logger) error {
+
+	pip_req := &pip.PointInPolygonRequest{
+		Latitude:            opts.Latitude,
+		Longitude:           opts.Longitude,
+		Placetypes:          opts.Placetypes,
+		Geometries:          opts.Geometries,
+		AlternateGeometries: opts.AlternateGeometries,
+		IsCurrent:           opts.IsCurrent,
+		IsCeased:            opts.IsCeased,
+		IsDeprecated:        opts.IsDeprecated,
+		IsSuperseded:        opts.IsSuperseded,
+		IsSuperseding:       opts.IsSuperseding,
+		InceptionDate:       opts.InceptionDate,
+		CessationDate:       opts.CessationDate,
+		Properties:          opts.Properties,
+		Sort:                opts.Sort,
+	}
+
+	spatial_req, err := request.NewPointInPolygonRequest(pip_req)
+
+	if err != nil {
+		return fmt.Errorf("Failed to create spatial PIP request, %w", err)
+	}
+
+	var grpc_opts []grpc.DialOption
+	grpc_opts = append(grpc_opts, grpc.WithInsecure())
+
+	addr := fmt.Sprintf("%s:%d", opts.Host, opts.Port)
+
+	conn, err := grpc.Dial(addr, grpc_opts...)
+
+	if err != nil {
+		return fmt.Errorf("Failed to dial '%s', %v", addr, err)
+	}
+
+	defer conn.Close()
+
+	client := spatial.NewSpatialClient(conn)
+
+	stream, err := client.PointInPolygon(ctx, spatial_req)
+
+	if err != nil {
+		return fmt.Errorf("Failed to perform point in polygon operation, %w", err)
+	}
+
+	writers := make([]io.Writer, 0)
+
+	if opts.Stdout {
+		writers = append(writers, os.Stdout)
+	}
+
+	if opts.Null {
+		writers = append(writers, io.Discard)
+	}
+
+	wr := io.MultiWriter(writers...)
+
+	enc := json.NewEncoder(wr)
+	err = enc.Encode(stream)
+
+	if err != nil {
+		return fmt.Errorf("Failed to encode stream, %v", err)
+	}
+
+	return nil
+}
diff --git a/vendor/github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/client/flags.go b/vendor/github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/client/flags.go
new file mode 100644
index 00000000..e6b2bdff
--- /dev/null
+++ b/vendor/github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/client/flags.go
@@ -0,0 +1,69 @@
+package client
+
+import (
+	"flag"
+
+	"github.com/sfomuseum/go-flags/flagset"
+	"github.com/sfomuseum/go-flags/multi"
+)
+
+var host string
+var port int
+
+var latitude float64
+var longitude float64
+var geometries string
+
+var inception string
+var cessation string
+
+var props multi.MultiString
+var placetypes multi.MultiString
+var alt_geoms multi.MultiString
+
+var is_current multi.MultiInt64
+var is_ceased multi.MultiInt64
+var is_deprecated multi.MultiInt64
+var is_superseded multi.MultiInt64
+var is_superseding multi.MultiInt64
+
+var sort_uris multi.MultiString
+
+var stdout bool
+var null bool
+
+func DefaultFlagSet() (*flag.FlagSet, error) {
+
+	fs := flagset.NewFlagSet("client")
+
+	fs.StringVar(&host, "host", "localhost", "The host of the gRPC server to connect to.")
+	fs.IntVar(&port, "port", 8082, "The port of the gRPC server to connect to.")
+
+	fs.BoolVar(&stdout, "stdout", true, "Emit results to STDOUT")
+	fs.BoolVar(&null, "null", false, "Emit results to /dev/null")
+
+	// query flags
+
+	fs.Float64Var(&latitude, "latitude", 0.0, "A valid latitude.")
+	fs.Float64Var(&longitude, "longitude", 0.0, "A valid longitude.")
+
+	fs.StringVar(&geometries, "geometries", "all", "Valid options are: all, alt, default.")
+
+	fs.StringVar(&inception, "inception", "", "A valid EDTF date string.")
+	fs.StringVar(&cessation, "cessation", "", "A valid EDTF date string.")
+
+	fs.Var(&props, "property", "One or more Who's On First properties to append to each result.")
+	fs.Var(&placetypes, "placetype", "One or more place types to filter results by.")
+
+	fs.Var(&alt_geoms, "alternate-geometry", "One or more alternate geometry labels (wof:alt_label) values to filter results by.")
+
+	fs.Var(&is_current, "is-current", "One or more existential flags (-1, 0, 1) to filter results by.")
+	fs.Var(&is_ceased, "is-ceased", "One or more existential flags (-1, 0, 1) to filter results by.")
+	fs.Var(&is_deprecated, "is-deprecated", "One or more existential flags (-1, 0, 1) to filter results by.")
+	fs.Var(&is_superseded, "is-superseded", "One or more existential flags (-1, 0, 1) to filter results by.")
+	fs.Var(&is_superseding, "is-superseding", "One or more existential flags (-1, 0, 1) to filter results by.")
+
+	fs.Var(&sort_uris, "sort-uri", "Zero or more whosonfirst/go-whosonfirst-spr/sort URIs.")
+
+	return fs, nil
+}
diff --git a/vendor/github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/client/options.go b/vendor/github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/client/options.go
new file mode 100644
index 00000000..e25ba006
--- /dev/null
+++ b/vendor/github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/client/options.go
@@ -0,0 +1,63 @@
+package client
+
+import (
+	"context"
+	"flag"
+
+	"github.com/sfomuseum/go-flags/flagset"
+)
+
+type RunOptions struct {
+	Host                string   `json:"host"`
+	Port                int      `json:"port"`
+	Stdout              bool     `json:"stdout"`
+	Null                bool     `json:"null"`
+	Latitude            float64  `json:"latitude"`
+	Longitude           float64  `json:"longitude"`
+	Placetypes          []string `json:"placetypes,omitempty"`
+	Geometries          string   `json:"geometries,omitempty"`
+	AlternateGeometries []string `json:"alternate_geometries,omitempty"`
+	IsCurrent           []int64  `json:"is_current,omitempty"`
+	IsCeased            []int64  `json:"is_ceased,omitempty"`
+	IsDeprecated        []int64  `json:"is_deprecated,omitempty"`
+	IsSuperseded        []int64  `json:"is_superseded,omitempty"`
+	IsSuperseding       []int64  `json:"is_superseding,omitempty"`
+	InceptionDate       string   `json:"inception_date,omitempty"`
+	CessationDate       string   `json:"cessation_date,omitempty"`
+	Properties          []string `json:"properties,omitempty"`
+	Sort                []string `json:"sort,omitempty"`
+}
+
+func RunOptionsFromFlagSet(ctx context.Context, fs *flag.FlagSet) (*RunOptions, error) {
+
+	flagset.Parse(fs)
+
+	err := flagset.SetFlagsFromEnvVars(fs, "WHOSONFIRST")
+
+	if err != nil {
+		return nil, err
+	}
+
+	opts := &RunOptions{
+		Host:                host,
+		Port:                port,
+		Stdout:              stdout,
+		Null:                null,
+		Latitude:            latitude,
+		Longitude:           longitude,
+		Placetypes:          placetypes,
+		Geometries:          geometries,
+		AlternateGeometries: alt_geoms,
+		IsCurrent:           is_current,
+		IsCeased:            is_ceased,
+		IsDeprecated:        is_deprecated,
+		IsSuperseded:        is_superseded,
+		IsSuperseding:       is_superseding,
+		InceptionDate:       inception,
+		CessationDate:       cessation,
+		Properties:          props,
+		Sort:                sort_uris,
+	}
+
+	return opts, nil
+}
diff --git a/vendor/github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/server/flags.go b/vendor/github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/server/flags.go
index 7cd7b753..f753c2df 100644
--- a/vendor/github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/server/flags.go
+++ b/vendor/github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/server/flags.go
@@ -29,8 +29,8 @@ func DefaultFlagSet() (*flag.FlagSet, error) {
 
 	fs := flagset.NewFlagSet("server")
 
-	fs.StringVar(&host, "host", "localhost", "...")
-	fs.IntVar(&port, "port", 8082, "...")
+	fs.StringVar(&host, "host", "localhost", "The host to listen for requests on")
+	fs.IntVar(&port, "port", 8082, "The port to listen for requests on")
 
 	available_databases := database.Schemes()
 	desc_databases := fmt.Sprintf("A valid whosonfirst/go-whosonfirst-spatial/data.SpatialDatabase URI. options are: %s", available_databases)
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 83b2497f..4c292e6e 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -503,8 +503,9 @@ github.com/whosonfirst/go-whosonfirst-spatial/geo
 github.com/whosonfirst/go-whosonfirst-spatial/hierarchy
 github.com/whosonfirst/go-whosonfirst-spatial/hierarchy/filter
 github.com/whosonfirst/go-whosonfirst-spatial/pip
-# github.com/whosonfirst/go-whosonfirst-spatial-grpc v0.1.1
+# github.com/whosonfirst/go-whosonfirst-spatial-grpc v0.1.2
 ## explicit; go 1.22.2
+github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/client
 github.com/whosonfirst/go-whosonfirst-spatial-grpc/app/server
 github.com/whosonfirst/go-whosonfirst-spatial-grpc/request
 github.com/whosonfirst/go-whosonfirst-spatial-grpc/server