---
title: Searching
description: This page covers how to build and run search queries with the Go client using the typed API and the esdsl builders. For the full Elasticsearch search...
url: https://www.elastic.co/docs/reference/elasticsearch/clients/go/using-the-api/searching
products:
  - Elasticsearch
  - Elasticsearch Client
  - Elasticsearch Go Client
---

# Searching
This page covers how to build and run search queries with the Go client using the [typed API](https://www.elastic.co/docs/reference/elasticsearch/clients/go/typed-api) and the [`esdsl`](https://www.elastic.co/docs/reference/elasticsearch/clients/go/typed-api/esdsl) builders. For the full Elasticsearch search API reference, see the [Search API documentation](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-search). For raw-JSON searches, see the [low-level API](https://www.elastic.co/docs/reference/elasticsearch/clients/go/low-level-api).
```go
import "github.com/elastic/go-elasticsearch/v9/typedapi/esdsl"
```


## Running a search

As an example, let's search for a document with a field `name` with a value of `Foo` in the index named `index_name`. The `esdsl` query builders provide a concise, fluent syntax:
```go
res, err := es.Search().
    Index("index_name").
    Query(esdsl.NewMatchQuery("name", "Foo")).
    Do(context.Background())
```

It produces the following JSON:
```json
{
  "query": {
    "match": {
      "name": {
        "query": "Foo"
      }
    }
  }
}
```


## Request structures

If you prefer working with typed structs directly, you can pass a `search.Request` to `Request`:
```go
res, err := es.Search().
    Index("index_name").
    Request(&search.Request{
        Query: &types.Query{
            Term: map[string]types.TermQuery{
                "name": {Value: "Foo"},
            },
        },
    }).Do(context.Background())
```

The `esdsl` builders and struct-based requests produce identical payloads; mix and match based on how complex the query is.

## Paginating results

A `Search` call returns at most `size` hits per request (defaulting to 10). Two strategies cover almost every pagination need:
- **`from` + `size`** is the simplest option. It works well for small result sets, but Elasticsearch refuses to page past the 10,000th hit by default (controlled by `index.max_result_window`). Raising that setting becomes expensive fast because each shard has to materialise and sort `from + size` hits per request.
- **`search_after`** combined with a **point in time (PIT)** is the recommended approach for deep pagination and for exports where a consistent view across pages matters. The PIT pins a snapshot of the index so concurrent writes do not shift results between pages.

Both examples below use the [`esdsl`](https://www.elastic.co/docs/reference/elasticsearch/clients/go/typed-api/esdsl) builders with the typed client. The [`_examples/search/pagination.go`](https://github.com/elastic/go-elasticsearch/blob/main/_examples/search/pagination.go) example is a runnable walkthrough of both strategies. See [Paginate search results](https://www.elastic.co/docs/reference/elasticsearch/rest-apis/paginate-search-results) for the full Elasticsearch reference.

### `from` + `size`

```go
const pageSize = 50
for page := 0; ; page++ {
    res, err := es.Search().
        Index("index_name").
        Query(esdsl.NewMatchAllQuery()).
        From(page * pageSize).
        Size(pageSize).       
        Do(ctx)
    if err != nil {
        log.Fatal(err)
    }
    for _, hit := range res.Hits.Hits {
        _ = hit
    }
    if len(res.Hits.Hits) < pageSize {
        break
    }
}
```


### PIT + `search_after`

```go
import (
    "github.com/elastic/go-elasticsearch/v9/typedapi/esdsl"
    "github.com/elastic/go-elasticsearch/v9/typedapi/types/enums/sortorder"
)
```

```go
pit, err := es.OpenPointInTime("index_name").KeepAlive("1m").Do(ctx)
if err != nil {
    log.Fatal(err)
}
defer es.ClosePointInTime().Id(pit.Id).Do(ctx)

req := es.Search().
    Query(esdsl.NewMatchAllQuery()).
    Pit(esdsl.NewPointInTimeReference().
        Id(pit.Id).
        KeepAlive(esdsl.NewDuration().String("1m"))).
    Sort(esdsl.NewSortOptions().
        AddSortOption("_shard_doc", esdsl.NewFieldSort(sortorder.Asc))).
    Size(1000)

for {
    res, err := req.Do(ctx)
    if err != nil {
        log.Fatal(err)
    }
    if len(res.Hits.Hits) == 0 {
        break
    }
    for _, hit := range res.Hits.Hits {
        _ = hit
    }
    last := res.Hits.Hits[len(res.Hits.Hits)-1]
    req = req.SearchAfterValues(last.Sort)
    if res.PitId != nil {
        req = req.Pit(esdsl.NewPointInTimeReference().
            Id(*res.PitId).
            KeepAlive(esdsl.NewDuration().String("1m")))
    }
}
```

The older `scroll` API still works but is no longer the recommended approach for deep pagination; prefer PIT + `search_after`.

## Raw JSON

If you want to use your own pre-baked JSON queries using templates or a specific encoder, you can pass the body directly with `Raw`:
```go
res, err := es.Search().
    Index("index_name").
    Raw([]byte(`{
      "query": {
        "term": {
          "user.id": {
            "value": "kimchy",
            "boost": 1.0
          }
        }
      }
    }`)).Do(context.Background())
```

No further validation or serialization is done on what is sent through this method. Setting a payload with `Raw` takes precedence over any request structure you may submit before running the query.