go-elasticsearch: the official Go client for Elasticsearch, v9 and v8 side by side
The official Go client for Elasticsearch
At a glance
- What is it?
- The official Go client for Elasticsearch ships a low-level esapi layer, a typed API with esdsl query builders, and the esutil helpers. Here is how it installs, what the two API styles cost you, and where the client stops being the right tool.
- Who is it for?
- Adopt go-elasticsearch when your Go service talks to an Elasticsearch cluster and you want the vendor's own client, the typed API, and esutil.BulkIndexer for ingestion. Do not adopt it if you run OpenSearch, or if your Go version is older than the 1.25.0 the module declares, since the module's go directive will force an upgrade or a downgrade of the client.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What go-elasticsearch is for, and who ends up using it
go-elasticsearch is the official Go client for Elasticsearch, published by Elastic under the module path github.com/elastic/go-elasticsearch/v9. It exists so that Go services do not have to hand-roll HTTP calls, JSON encoding and connection pooling against an Elasticsearch cluster. The README frames it as the official client and points to elastic.co for the full reference documentation, with the Go package reference on pkg.go.dev.
The audience is narrow but deep: backend teams writing indexers, search endpoints, log pipelines and internal tooling in Go. If your application already speaks HTTP and you only ever call one endpoint, the client is more machinery than you need. If you are writing a service that indexes documents continuously, searches them, and has to survive node failures, the client is where the retry logic, the connection pool and the request encoding already live.
One detail matters before you write a line of code. The module path carries the major version. The README states that when using Go modules you include the version in the import path and specify either an explicit version or a branch, giving the examples v9.x.x and v8.x.x. That is not cosmetic. It is the mechanism that lets a single project depend on two client majors at once, which the README demonstrates with a go.mod listing both v8.18.0 and v9.0.0 and a main.go importing them as elasticsearch8 and elasticsearch9.
Two API surfaces: low-level esapi and the typed API with esdsl
The repository layout shows the split plainly. At the top level sit elasticsearch.go and options.go, plus the directories esapi, esutil, typedapi and internal. The README's documentation list names the two ways of using the client: the low-level API and the typed API, with esdsl builders documented alongside the typed API.
The low-level layer is the esapi package. You construct a request, hand it a JSON body, and get a response back with the raw bytes. That is the honest description of what it does: it moves bytes and manages the transport. The trade-off is that nothing checks your query before it reaches the cluster. A misspelled field name is a valid JSON document and an invalid query, and you find out from the cluster's error response rather than from the compiler.
The typed API and esdsl builders are the other direction. Queries are built from Go values, so the shape of a query is expressed in code the compiler can inspect. The cost is coupling: typed structures track a specific Elasticsearch version's API surface, and the README's compatibility section states that language clients are forward compatible, supporting communication with greater or equal minor versions of Elasticsearch, and are only backward compatible with default distributions and without guarantees made. Read that sentence twice. Forward compatible means a v9 client can talk to a newer minor. It does not promise that a newer client speaks cleanly to an older cluster.
Underneath both surfaces the transport is not written here. The go.mod requires github.com/elastic/elastic-transport-go/v8 v8.11.0, so connection handling, retries and node selection live in a separate module. OpenTelemetry tracing is a direct dependency too, at go.opentelemetry.io/otel/trace v1.43.0, which tells you observability was designed in rather than bolted on.
Installing go-elasticsearch and running a first search
The README does not inline installation steps. It says to refer to the Installation page of the documentation, and the same for Connecting. What the README does give is the module requirement form, which is the part you actually need in your own go.mod. Note the go directive in the repository's go.mod: go 1.25.0, with toolchain go1.25.12. If your project is on an older Go release, adding this dependency will pull your build forward.
Add the module at the major version that matches your cluster:
go get github.com/elastic/go-elasticsearch/v9In go.mod the require line looks like this, matching the README's example form:
require github.com/elastic/go-elasticsearch/v9 v9.x.xTo use two majors in one project, the README gives this pattern, with both versions listed in go.mod and both imported under aliases:
import (
elasticsearch8 "github.com/elastic/go-elasticsearch/v8"
elasticsearch9 "github.com/elastic/go-elasticsearch/v9"
)Constructing a client is a single call, as the README shows for each alias:
es8, _ := elasticsearch8.New()
es9, _ := elasticsearch9.New()The README's example discards the error, which is fine for a snippet and wrong in a service. The client's configuration options, TLS setup and custom certificate authority handling are covered in the _examples folder and the Configuration and Advanced topics documentation pages, not in the README itself, so budget time for those pages before you wire the client into production. For bulk work, the README points at esutil.BulkIndexer, documented on the bulk indexing page.
Where the client is the wrong tool
The compatibility section is the sharpest edge here. Forward compatibility means the client supports communicating with greater or equal minor versions of Elasticsearch. Backward compatibility is qualified: only with default distributions, and without guarantees made. If you run a vendor distribution, or you upgrade the client before the cluster, you are outside what the documentation promises. That is not a bug to report; it is a stated boundary.
The second boundary is the search question that keeps appearing in Google's data about this project: why companies move away from Elasticsearch, and what its downsides are. Those questions are about the server, not this library, and the client cannot answer them. If your reason for leaving Elasticsearch is licensing or operational cost, swapping the Go client changes nothing. The client is a thin layer over HTTP; the cluster is the thing you would be replacing.
Third, if you run OpenSearch, this is the wrong client. The related searches include Go OpenSearch, and that is a different project with a different module path. The README never mentions OpenSearch, and the API surface here tracks Elasticsearch's own releases, including the typed API that encodes Elasticsearch's request shapes. Using this client against a forked server means you own every divergence yourself.
Finally, the module's go directive of 1.25.0 is a real constraint for teams on long-term-support Go toolchains. You cannot quietly add this dependency to a codebase pinned to an older release without changing the toolchain.
The alternative: a generic HTTP client, and what you give up
The realistic alternative is not another Elasticsearch client library. It is net/http plus encoding/json, talking to the cluster's REST endpoints directly. That approach has one genuine advantage: nothing to upgrade. If you call two endpoints with fixed query bodies, a hundred lines of Go will do it, and you will never chase a client major version.
What you give up is everything the transport module does. The go.mod shows github.com/elastic/elastic-transport-go/v8 v8.11.0 as a direct requirement, and the README's Advanced topics page lists interceptors and observability. Connection pooling across cluster nodes, retry behaviour, and the OpenTelemetry tracing integration all sit in that layer. Reimplementing them is possible and usually regretted, because the failure modes only appear under load, when a node goes away mid-request.
The second thing you give up is the typed API. With hand-written JSON, every query is a string you cannot type-check. With esdsl builders, the query is Go code. Teams that index heavily and search rarely often stay on the low-level esapi layer deliberately, because the raw JSON is easier to copy from documentation and from the cluster's own query output. Teams with many query shapes tend to move to the typed API. Both are supported in the same module, which is the point of shipping them together.
Maintenance, release cadence and the Apache 2 licence
The repository is not archived, and the last push was on 2026-09-18. Recent releases are close together: v9.5.0 on 2026-08-04, v9.5.1 on 2026-08-25, and v9.5.2 on 2026-09-01. That cadence means patch releases arrive on a roughly monthly rhythm, and it also means you should pin an exact version rather than tracking a branch, which is exactly what the README's require-line examples do.
Upgrade cost is dominated by the major version boundary, not the patch releases. The module path encodes the major, so moving from v8 to v9 is an import-path change plus whatever typed API structures moved. The README's demonstration that v8 and v9 can coexist in one go.mod is the practical escape hatch: you can migrate package by package instead of in one commit, running both clients against the same cluster during the transition.
The licence is Apache-2.0, stated in the README's License section, with a separate NOTICE file at the repository root. Apache-2.0 is permissive and includes a patent grant, which is why it is common in infrastructure libraries. The NOTICE file exists because Apache-2.0 requires attribution to be preserved; if you redistribute the client, keep it. That is a description of the licence text, not legal advice, and your own counsel should review anything you ship.
The Makefile shows how the project tests itself: test-unit runs go test with coverage, and test-integ runs integration tests against a real cluster, pulling common YAML test suites from the elasticsearch-clients-tests repository. That integration suite is the reason compatibility claims are testable rather than aspirational, and it also tells you the client is validated against a live cluster, not only against mocks.
Editorial conclusion
Adopt go-elasticsearch when your Go service talks to an Elasticsearch cluster and you want the vendor's own client, the typed API, and esutil.BulkIndexer for ingestion. Do not adopt it if you run OpenSearch, or if your Go version is older than the 1.25.0 the module declares, since the module's go directive will force an upgrade or a downgrade of the client. Before writing application code, confirm which Elasticsearch minor version your cluster runs, because the import path carries the major version and mixing v8 and v9 in one binary is supported but deliberate. Then check whether you need the typed API or the low-level esapi layer, since that choice shapes every call you write afterwards.
Frequently asked questions
Which import path should a new Go project use for go-elasticsearch?
The README shows the module path with the major version embedded, github.com/elastic/go-elasticsearch/v9, and states that when using Go modules you include the version in the import path. The repository's own go.mod declares module github.com/elastic/go-elasticsearch/v9. Pick the major that matches the Elasticsearch version you connect to.
Can a Go project use go-elasticsearch v8 and v9 at the same time?
Yes. The README states it is possible to use multiple versions of the client in a single project, and gives an example go.mod listing both v8.18.0 and v9.0.0, with main.go importing them under the aliases elasticsearch8 and elasticsearch9.
What Go version does go-elasticsearch require?
The repository's go.mod declares go 1.25.0 with toolchain go1.25.12. The README says that starting from version 8.12.0 the library follows Go's release policy, where each major Go release is supported until two newer major releases exist.
Does go-elasticsearch include helpers for bulk indexing?
Yes. The README states that the esutil package provides convenience helpers, currently esutil.JSONReader() and esutil.BulkIndexer, and links the bulk indexing page of the documentation for details.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/elastic-go-elasticsearch)