﻿---
title: Low-level API
description: The low-level API provides a one-to-one mapping with the Elasticsearch REST API. Each endpoint accepts raw io.Reader request bodies and returns *esapi.Response...
url: https://www.elastic.co/elastic/docs-builder/docs/3167/reference/elasticsearch/clients/go/low-level-api
products:
  - Elasticsearch Client
  - Elasticsearch Go Client
---

# Low-level API
The low-level API provides a one-to-one mapping with the Elasticsearch REST API. Each endpoint accepts raw `io.Reader` request bodies and returns `*esapi.Response` objects whose `Body` is an `io.ReadCloser` you read and close yourself.
The rest of this documentation is built around the [typed API](https://www.elastic.co/elastic/docs-builder/docs/3167/reference/elasticsearch/clients/go/typed-api) and the [esdsl builders](https://www.elastic.co/elastic/docs-builder/docs/3167/reference/elasticsearch/clients/go/typed-api/esdsl), which together cover the vast majority of Elasticsearch endpoints with compile-time safety, automatic JSON encoding and decoding, and fluent query construction. **We recommend the typed API for new code.** If you are already using the low-level API, the [migration guide](https://www.elastic.co/elastic/docs-builder/docs/3167/reference/elasticsearch/clients/go/low-level-api/migration) shows how to adopt the typed API without rewriting your whole codebase.

## When to use the low-level API

Reach for the low-level API when:
- You need an endpoint that is not covered by the typed API.
- You want full control over request serialization, for example to plug in a custom JSON encoder or a streaming body.
- You are working with pre-baked JSON payloads and do not want to model them as Go structs.

If none of these apply, prefer the [typed API](https://www.elastic.co/elastic/docs-builder/docs/3167/reference/elasticsearch/clients/go/typed-api).

## Creating a client

Use `elasticsearch.New` with functional options:
```go
package main

import (
    "context"
    "log"

    "github.com/elastic/go-elasticsearch/v9"
)

func main() {
    client, err := elasticsearch.New(
        elasticsearch.WithAddresses("https://localhost:9200"),
        elasticsearch.WithAPIKey("API_KEY"),
    )
    if err != nil {
        log.Fatalf("creating client: %s", err)
    }
    defer client.Close(context.Background())

    res, err := client.Info()
    if err != nil {
        log.Fatalf("info: %s", err)
    }
    defer res.Body.Close()
    log.Println(res)
}
```

For full configuration options, see the [Configuration reference](https://www.elastic.co/elastic/docs-builder/docs/3167/reference/elasticsearch/clients/go/configuration).
<note>
  `NewClient(Config)` and `NewDefaultClient()` still work but are deprecated. Use `New` with [functional options](https://www.elastic.co/elastic/docs-builder/docs/3167/reference/elasticsearch/clients/go/configuration) instead.
</note>


## Response handling

Every low-level call returns an `*esapi.Response` whose body you must read and close. Failing to close the body prevents Go's HTTP client from reusing the underlying TCP connection.
```go
res, err := client.Search(
    client.Search.WithIndex("my-index"),
    client.Search.WithBody(strings.NewReader(`{"query":{"match_all":{}}}`)),
)
if err != nil {
    log.Fatal(err)
}
defer res.Body.Close()

if res.IsError() {
    log.Fatalf("search failed: %s", res.String())
}

var result map[string]any
if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
    log.Fatal(err)
}
```


## Common operations

A condensed tour of the most common CRUD and search calls. For detailed tutorials, see the [typed API equivalents](https://www.elastic.co/elastic/docs-builder/docs/3167/reference/elasticsearch/clients/go/using-the-api); the low-level signatures below are stable and map directly to the corresponding REST endpoints.

### Create an index

```go
mapping := `{"mappings":{"properties":{"price":{"type":"integer"}}}}`

res, err := client.Indices.Create(
    "test-index",
    client.Indices.Create.WithBody(strings.NewReader(mapping)),
)
if err != nil {
    log.Fatal(err)
}
defer res.Body.Close()
```


### Index a document

```go
document := struct {
    Name  string `json:"name"`
    Price int    `json:"price"`
}{Name: "Foo", Price: 10}

data, _ := json.Marshal(document)
res, err := client.Index(
    "test-index",
    bytes.NewReader(data),
    client.Index.WithDocumentID("1"),
)
if err != nil {
    log.Fatal(err)
}
defer res.Body.Close()
```


### Get a document

```go
res, err := client.Get("test-index", "1")
if err != nil {
    log.Fatal(err)
}
defer res.Body.Close()
```


### Search

```go
query := `{"query":{"match":{"name":{"query":"Foo"}}}}`

res, err := client.Search(
    client.Search.WithIndex("test-index"),
    client.Search.WithBody(strings.NewReader(query)),
)
if err != nil {
    log.Fatal(err)
}
defer res.Body.Close()
```


### Delete a document

```go
res, err := client.Delete("test-index", "1")
if err != nil {
    log.Fatal(err)
}
defer res.Body.Close()
```


### Bulk

```go
var buf bytes.Buffer
buf.WriteString(`{"index":{"_index":"test","_id":"1"}}` + "\n")
buf.WriteString(`{"title":"Test"}` + "\n")

res, err := client.Bulk(bytes.NewReader(buf.Bytes()))
if err != nil {
    log.Fatal(err)
}
defer res.Body.Close()
```

For a higher-level alternative that handles batching, flushing, and concurrency, see [`esutil.BulkIndexer`](/elastic/docs-builder/docs/3167/reference/elasticsearch/clients/go/using-the-api/bulk-indexing#_bulk_indexer_helper). The BulkIndexer works with both the low-level and typed clients.

## API reference

The full set of low-level endpoints, parameters, and option functions is documented on pkg.go.dev:
- [`esapi` package reference](https://pkg.go.dev/github.com/elastic/go-elasticsearch/v9/esapi)


## Migrating to the typed API

You do not have to switch everything at once. An `*elasticsearch.Client` already satisfies the transport interface used by the typed API packages, so you can adopt typed endpoints one at a time while keeping the rest of your code untouched. See the [migration guide](https://www.elastic.co/elastic/docs-builder/docs/3167/reference/elasticsearch/clients/go/low-level-api/migration) for the details.