go · zero deps · arcgis rest
A Go client for ArcGIS Feature Services.
Query ArcGIS Feature Services over their REST API — with automatic pagination, counts, ID-only queries, and spatial filters. Two interchangeable styles: a plain struct API and a fluent builder.
MIT licensed · no third-party dependencies · Go 1.26+
client := arcgis.NewClient(baseURL) features, _ := client.Layer(7).Query(). Where("STAGE = 4"). Fields("BLOCK_NAME", "STAGE"). All(ctx) // paginates automatically
what you get
Feature Services, without the boilerplate.
One small engine behind two call styles — it handles the paging, the counting, and ArcGIS's quirks so you don't have to.
Two styles, one engine
A fluent builder and an explicit struct API produce the same QueryParams and hit the same code path — pick whichever reads better at the call site.
Automatic pagination
QueryAll / .All keeps advancing resultOffset while the service reports exceededTransferLimit — you get every feature, not just the first page.
Counts & IDs, cheaply
.Count returns an int with no feature data transferred; .IDs returns just the object IDs — perfect for pre-flighting a big query.
Spatial filters
Filter by a bounding box with WithinEnvelope, or by a point with IntersectsPoint and an explicit SpatialRel.
GeoJSON or Esri JSON
GeoJSON by default; switch to Esri JSON for richer metadata. Feature.Attrs() returns attributes uniformly regardless of the response format.
Zero deps, typed errors
Pure Go, no third-party imports. ArcGIS often reports failures with HTTP 200 and an error envelope — these surface as a typed *APIError you can match with errors.As.
usage
Struct or fluent — same result.
Both compile to identical QueryParams. Use the struct form when you want something explicit and serializable; the builder when you want it to read like a sentence.
// Struct style — explicit, serializable fs, err := client.Query(ctx, arcgis.QueryParams{ LayerID: 7, Where: "STAGE = 4", Fields: []string{"BLOCK_NAME", "STAGE"}, PageSize: 100, })
// Fluent style — readable, chainable features, err := client.Layer(7).Query(). Where("STAGE = 4"). Fields("BLOCK_NAME", "STAGE"). WithinEnvelope(18.4, -34.0, 18.6, -33.8). All(ctx)
// Cheap pre-flight: count, then IDs n, _ := client.Layer(7).Query().Where("1=1").Count(ctx) ids, _ := client.Layer(7).Query().IDs(ctx)
// ArcGIS errors arrive as HTTP 200 + envelope var apiErr *arcgis.APIError if errors.As(err, &apiErr) { fmt.Println(apiErr.Code, apiErr.Message) }
- Query / .First
- QueryAll / .All
- QueryCount / .Count
- QueryIDs / .IDs
- WithToken
- WithTimeout
- WithHTTPClient
install
Add it to your module.
One import, no transitive dependencies. Requires Go 1.26+.
go get github.com/richardwooding/go-arcgis
import "github.com/richardwooding/go-arcgis"
Need verified City of Cape Town layer IDs? The companion capetown-opendata package ships ready-made constructors. A PHP port tracks this design too.