Repository: opensearch-project/opensearch-go
Pull request #257 (2023-03-21 10:16:50)

Add operations with data streams
### Description
Implemented Data Stream API: 
 * Create a data stream
 * Delete a data stream
 * Get all data streams
 * Get specific data stream
 * Get Stats of specific data stream  

Planned to add:
* Ingest data into the data stream
* Searching a data stream
* Rollover a data stream

### Issues Resolved
Closes [#152]

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check [here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).


dblock: Looks like some failures in tests, but otherwise looks good!
zethuman: Yes, my bad, did not look at the instructions on the Makefile. It seems that I wrote not unit tests, so thinking about adding integration tags, since I need an opensource instance for the test. What do you think?

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e30940e..ddb9905 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,7 +22,9 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
 
 - Github workflow for changelog verification ([#172](https://github.com/opensearch-project/opensearch-go/pull/172))
 - Add Go Documentation link for the client ([#182](https://github.com/opensearch-project/opensearch-go/pull/182))
+- Add implementation of Data Streams API ([#257](https://github.com/opensearch-project/opensearch-go/pull/257)
 - Support for Amazon OpenSearch Serverless ([#216](https://github.com/opensearch-project/opensearch-go/pull/216), [#259](https://github.com/opensearch-project/opensearch-go/pull/259))
+- Add Err() function to Response for detailed errors ([#246](https://github.com/opensearch-project/opensearch-go/pull/246))
 
 ### Dependencies
 
@@ -32,6 +34,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
 ### Changed
 
 - Workflow improvements ([#242](https://github.com/opensearch-project/opensearch-go/pull/242))
+- Opensearchapi check the response for errors ([#246](https://github.com/opensearch-project/opensearch-go/pull/246))
 
 ### Deprecated
 
diff --git a/UPGRADING.md b/UPGRADING.md
new file mode 100644
index 0000000..48dc981
--- /dev/null
+++ b/UPGRADING.md
@@ -0,0 +1,33 @@
+- [Upgrading Opensearch GO Client](#upgrading-opensearch-go-client)
+    - [Upgraading to >= 3.0.0](#upgrading-to->=-3.0.0)
+        - [opensearchapi](#opensearchapi-error-handling)
+
+# Upgrading Opensearch GO Client
+
+## Upgrading to >= 3.0.0
+
+### opensearchapi error handling
+With opensearch-go >= 3.0.0 opensearchapi responses are now checked for errors.
+Prior versions only returned an error if the request failed to execute. For example if the client can't reach the server or the TLS handshake failed.
+With opensearch-go >= 3.0.0 each opensearchapi requests will return an error if the response http status code is > 299.
+The error can be parsed into the new opensearchapi.Error type by using `errors.As` to match for exceptions and get a more detailed view. See the example below.
+
+```go
+	createIndex := opensearchapi.IndicesCreateRequest{
+		Index: IndexName,
+		Body:  mapping,
+	}
+	ctx := context.Background()
+	var opensearchError *opensearchapi.Error
+	createIndexResponse, err := createIndex.Do(ctx, client)
+	// Load err into opensearchapi.Error to access the fields and tolerate if the index already exists
+	if err != nil {
+		if errors.As(err, &opensearchError) {
+			if opensearchError.Err.Type != "resource_already_exists_exception" {
+				return err
+			}
+		} else {
+			return err
+		}
+	}
+```
diff --git a/USER_GUIDE.md b/USER_GUIDE.md
index 58cd71c..db4a128 100644
--- a/USER_GUIDE.md
+++ b/USER_GUIDE.md
@@ -1,6 +1,14 @@
 - [User Guide](#user-guide)
-  - [Example](#example)
-  - [Amazon OpenSearch Service](#amazon-opensearch-service) - [AWS SDK V1](#aws-sdk-v1) - [AWS SDK V2](#aws-sdk-v2)
+	- [Example](#example)
+	- [Amazon OpenSearch Service](#amazon-opensearch-service)
+			- [AWS SDK V1](#aws-sdk-v1)
+			- [AWS SDK V2](#aws-sdk-v2)
+	- [Data Streams API](#data-streams-api)
+		- [Create Data Stream](#create-data-streams)
+		- [Delete Data Stream](#delete-data-streams)
+		- [Get All Data Streams](#get-data-streams)
+		- [Get Specific Data Stream](#get-specific-data-streams)
+		- [Get Specific Data Stream Stats](#get-specific-data-streams-stats)
 
 # User Guide
 
@@ -13,19 +21,28 @@ search for the document, delete the document and finally delete the index.
 package main
 
 import (
-	"os"
 	"context"
 	"crypto/tls"
+	"errors"
 	"fmt"
-	opensearch "github.com/opensearch-project/opensearch-go/v2"
-	opensearchapi "github.com/opensearch-project/opensearch-go/v2/opensearchapi"
 	"net/http"
+	"os"
 	"strings"
+
+	opensearch "github.com/opensearch-project/opensearch-go/v2"
+	opensearchapi "github.com/opensearch-project/opensearch-go/v2/opensearchapi"
 )
 
 const IndexName = "go-test-index1"
 
 func main() {
+	if err := example(); err != nil {
+		fmt.Println(fmt.Sprintf("Error: %s", err))
+		os.Exit(1)
+	}
+}
+
+func example() error {
 
 	// Initialize the client with SSL/TLS enabled.
 	client, err := opensearch.NewClient(opensearch.Config{
@@ -37,8 +54,7 @@ func main() {
 		Password:  "admin",
 	})
 	if err != nil {
-		fmt.Println("cannot initialize", err)
-		os.Exit(1)
+		return err
 	}
 
 	// Print OpenSearch version information on console.
@@ -58,10 +74,18 @@ func main() {
 		Index: IndexName,
 		Body:  mapping,
 	}
-	createIndexResponse, err := createIndex.Do(context.Background(), client)
+	ctx := context.Background()
+	var opensearchError *opensearchapi.Error
+	createIndexResponse, err := createIndex.Do(ctx, client)
+	// Load err into opensearchapi.Error to access the fields and tolerate if the index already exists
 	if err != nil {
-		fmt.Println("failed to create index ", err)
-		os.Exit(1)
+		if errors.As(err, &opensearchError) {
+			if opensearchError.Err.Type != "resource_already_exists_exception" {
+				return err
+			}
+		} else {
+			return err
+		}
 	}
 	fmt.Println(createIndexResponse)
 
@@ -78,10 +102,9 @@ func main() {
 		DocumentID: docId,
 		Body:       document,
 	}
-	insertResponse, err := req.Do(context.Background(), client)
+	insertResponse, err := req.Do(ctx, client)
 	if err != nil {
-		fmt.Println("failed to insert document ", err)
-		os.Exit(1)
+		return err
 	}
 	fmt.Println(insertResponse)
 
@@ -100,23 +123,21 @@ func main() {
 		Body: content,
 	}
 
-	searchResponse, err := search.Do(context.Background(), client)
+	searchResponse, err := search.Do(ctx, client)
 	if err != nil {
-		fmt.Println("failed to search document ", err)
-		os.Exit(1)
+		return err
 	}
 	fmt.Println(searchResponse)
 
 	// Delete the document.
-	delete := opensearchapi.DeleteRequest{
+	deleteReq := opensearchapi.DeleteRequest{
 		Index:      IndexName,
 		DocumentID: docId,
 	}
 
-	deleteResponse, err := delete.Do(context.Background(), client)
+	deleteResponse, err := deleteReq.Do(ctx, client)
 	if err != nil {
-		fmt.Println("failed to delete document ", err)
-		os.Exit(1)
+		return err
 	}
 	fmt.Println("deleting document")
 	fmt.Println(deleteResponse)
@@ -126,14 +147,26 @@ func main() {
 		Index: []string{IndexName},
 	}
 
-	deleteIndexResponse, err := deleteIndex.Do(context.Background(), client)
+	deleteIndexResponse, err := deleteIndex.Do(ctx, client)
 	if err != nil {
-		fmt.Println("failed to delete index ", err)
-		os.Exit(1)
+		return err
 	}
 	fmt.Println("deleting index", deleteIndexResponse)
-}
 
+	// Try to delete the index again which failes as it does not exist
+	// Load err into opensearchapi.Error to access the fields and tolerate if the index is missing
+	_, err = deleteIndex.Do(ctx, client)
+	if err != nil {
+		if errors.As(err, &opensearchError) {
+			if opensearchError.Err.Type != "index_not_found_exception" {
+				return err
+			}
+		} else {
+			return err
+		}
+	}
+	return nil
+}
 ```
 
 ## Amazon OpenSearch Service
@@ -207,17 +240,6 @@ func main() {
 	}
 	defer resp.Body.Close()
 
-	if resp.IsError() {
-		log.Printf("ping response status: %q", resp.Status())
-
-		respBody, err := io.ReadAll(resp.Body)
-		if err != nil {
-			log.Fatalf("failed to read response body body: %v", err)
-		}
-
-		log.Fatalf("ping resp body: %s", respBody)
-	}
-
 	log.Println("PING OK")
 }
 ```
@@ -287,7 +309,7 @@ func main() {
 		Index: indexName,
 		Body:  mapping,
 	}
-	createIndexResponse, err := createIndex.Do(context.Background(), client)
+	createIndexResponse, err := createIndex.Do(ctx, client)
 	if err != nil {
 		log.Fatalf("failed to create index: %v", err)
 	}
@@ -298,7 +320,7 @@ func main() {
 		Index: []string{indexName},
 	}
 
-	deleteIndexResponse, err := deleteIndex.Do(context.Background(), client)
+	deleteIndexResponse, err := deleteIndex.Do(ctx, client)
 	if err != nil {
 		log.Fatalf("failed to delete index: %v", err)
 	}
@@ -317,3 +339,166 @@ func getCredentialProvider(accessKey, secretAccessKey, token string) aws.Credent
 }
 
 ```
+
+## Data Streams API
+
+### Create Data Streams
+
+ - Create new client
+```
+client, err := opensearch.NewDefaultClient()
+if err != nil {
+    panic(err)
+}
+```
+
+ - Create template index
+
+```
+iPut := opensearchapi.IndicesPutIndexTemplateRequest{
+	Name:       "demo-data-template",
+	Pretty:     true,
+	Human:      true,
+	ErrorTrace: true,
+	Body:       strings.NewReader(`{"index_patterns": ["demo-*"], "data_stream": {}, "priority": 100} }`),
+}
+iPutResponse, err := iPut.Do(context.Background(), client)
+```
+
+ - Prepare request object
+```
+es := opensearchapi.IndicesCreateDataStreamRequest{
+	Name:       "demo-name",
+	Human:      true,
+	Pretty:     true,
+	ErrorTrace: true,
+	Header: map[string][]string{
+		"Content-Type": {"application/json"},
+	},
+}
+```
+
+ - Execute request
+```
+res, err := es.Do(context.TODO(), client)
+if err != nil {
+	// do not panic in production code
+	panic(err)
+}
+```
+
+ - Try to read response
+```
+defer res.Body.Close()
+body, err := ioutil.ReadAll(res.Body)
+if err != nil {
+	// do not panic in production code
+	panic(err)
+}
+
+fmt.Println("Response Status Code: ", res.StatusCode)
+fmt.Println("Response Headers: ", res.Header)
+fmt.Println("Response Body: ", string(body))
+```
+
+ - Successfully created data stream
+```
+Response Status Code: 200
+Response Headers:     map[Content-Length:[28] Content-Type:[application/json; charset=UTF-8]]
+Response Body:        {"acknowledged" : true}
+```
+
+### Delete Data Streams
+
+ - Create new client as previous example
+ - Prepare request object
+```
+opensearchapi.IndicesDeleteDataStreamRequest{
+	Name:       "demo-name",
+	Pretty:     true,
+	Human:      true,
+	ErrorTrace: true,
+	Header: map[string][]string{
+		"Content-Type": {"application/json"},
+	},
+}
+```
+- Execute request as previous example
+- Try to read response as previous example
+- Successfully deleted data stream 
+```
+Response Status Code: 200
+Response Headers:     map[Content-Length:[28] Content-Type:[application/json; charset=UTF-8]]
+Response Body:        {"acknowledged" : true}
+```
+
+### Get All Data Streams
+
+- Create new client as previous example
+- Prepare request object
+```
+r := opensearchapi.IndicesGetDataStreamRequest{
+	Pretty:     true,
+	Human:      true,
+	ErrorTrace: true,
+	Header: map[string][]string{
+		"Content-Type": {"application/json"},
+	},
+}
+```
+- Execute request as previous example
+- Try to read response as previous example
+- Successfully retrieved data streams
+```
+Response Status Code: 200
+Response Headers:     map[Content-Length:[28] Content-Type:[application/json; charset=UTF-8]]
+Response Body: 	      {"data_streams":[{"name":"demo-name","timestamp_field":{"name":"@timestamp"},"indices":[{"index_name":".ds-demo-2023-03-21-23-33-46-000001","index_uuid":"NnzgqnP0ThS7LOMHJuZ-VQ"}],"generation":1,"status":"YELLOW","template":"demo-data-template"}]}
+```
+
+### Get Specific Data Stream
+
+- Create new client as previous example
+- Prepare request object
+```
+r := opensearchapi.IndicesGetDataStreamRequest{
+		Name: 	 	"demo-name",
+		Pretty:     true,
+		Human:      true,
+		ErrorTrace: true,
+		Header: map[string][]string{
+			"Content-Type": {"application/json"},
+		},
+	}
+```
+- Execute request as previous example
+- Try to read response as previous example
+- Successfully retrieved data stream
+```
+Response Status Code: 200
+Response Headers:     map[Content-Length:[28] Content-Type:[application/json; charset=UTF-8]]
+Response Body:        {"data_streams":[{"name":"demo-name","timestamp_field":{"name":"@timestamp"},"indices":[{"index_name":".ds-demo-2023-03-21-23-31-50-000001","index_uuid":"vhsowqdeRFCmr1GgQ7mIsQ"}],"generation":1,"status":"YELLOW","template":"demo-data-template"}]}
+```
+
+### Get Specific Data Stream Stats
+
+- Create new client as as previous example
+- Prepare request object
+```
+r := opensearchapi.IndicesGetDataStreamStatsRequest{
+	Name:       "demo-name",
+	Pretty:     true,
+	Human:      true,
+	ErrorTrace: true,
+	Header: map[string][]string{
+		"Content-Type": {"application/json"},
+	},
+}
+```
+- Execute request as previous example
+- Try to read response as previous example
+- Successfully retrieved data stream stats
+```
+Response Status Code: 200
+Response Headers:     map[Content-Length:[28] Content-Type:[application/json; charset=UTF-8]]
+Response Body:        {"_shards":{"total":2,"successful":1,"failed":0},"data_stream_count":1,"backing_indices":1,"total_store_size":"208b","total_store_size_bytes":208,"data_streams":[{"data_stream":"demo-name","backing_indices":1,"store_size":"208b","store_size_bytes":208,"maximum_timestamp":0}]}
+```
\ No newline at end of file
diff --git a/opensearchapi/api._.go b/opensearchapi/api._.go
index 5382f00..82563f8 100755
--- a/opensearchapi/api._.go
+++ b/opensearchapi/api._.go
@@ -136,10 +136,12 @@ type Indices struct {
 	Clone                 IndicesClone
 	Close                 IndicesClose
 	Create                IndicesCreate
+	CreateDataStream      IndicesCreateDataStream
 	DeleteAlias           IndicesDeleteAlias
 	DeleteIndexTemplate   IndicesDeleteIndexTemplate
 	Delete                IndicesDelete
 	DeleteTemplate        IndicesDeleteTemplate
+	DeleteDataStream      IndicesDeleteDataStream
 	DiskUsage             IndicesDiskUsage
 	ExistsAlias           IndicesExistsAlias
 	ExistsIndexTemplate   IndicesExistsIndexTemplate
@@ -155,6 +157,8 @@ type Indices struct {
 	Get                   IndicesGet
 	GetSettings           IndicesGetSettings
 	GetTemplate           IndicesGetTemplate
+	GetDataStream         IndicesGetDataStream
+	GetDataStreamStats    IndicesGetDataStreamStats
 	GetUpgrade            IndicesGetUpgrade
 	Open                  IndicesOpen
 	PutAlias              IndicesPutAlias
@@ -314,10 +318,12 @@ func New(t Transport) *API {
 			Clone:                 newIndicesCloneFunc(t),
 			Close:                 newIndicesCloseFunc(t),
 			Create:                newIndicesCreateFunc(t),
+			CreateDataStream:      newIndicesCreateDataStreamFunc(t),
 			DeleteAlias:           newIndicesDeleteAliasFunc(t),
 			DeleteIndexTemplate:   newIndicesDeleteIndexTemplateFunc(t),
 			Delete:                newIndicesDeleteFunc(t),
 			DeleteTemplate:        newIndicesDeleteTemplateFunc(t),
+			DeleteDataStream:      newIndicesDeleteDataStreamFunc(t),
 			DiskUsage:             newIndicesDiskUsageFunc(t),
 			ExistsAlias:           newIndicesExistsAliasFunc(t),
 			ExistsIndexTemplate:   newIndicesExistsIndexTemplateFunc(t),
@@ -333,6 +339,8 @@ func New(t Transport) *API {
 			Get:                   newIndicesGetFunc(t),
 			GetSettings:           newIndicesGetSettingsFunc(t),
 			GetTemplate:           newIndicesGetTemplateFunc(t),
+			GetDataStream:         newIndicesGetDataStreamFunc(t),
+			GetDataStreamStats:    newIndicesGetDataStreamStatsFunc(t),
 			GetUpgrade:            newIndicesGetUpgradeFunc(t),
 			Open:                  newIndicesOpenFunc(t),
 			PutAlias:              newIndicesPutAliasFunc(t),
diff --git a/opensearchapi/api.bulk.go b/opensearchapi/api.bulk.go
index c104f0d..261deb0 100644
--- a/opensearchapi/api.bulk.go
+++ b/opensearchapi/api.bulk.go
@@ -196,7 +196,7 @@ func (r BulkRequest) Do(ctx context.Context, transport Transport) (*Response, er
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.aliases.go b/opensearchapi/api.cat.aliases.go
index 22cb459..608a13c 100644
--- a/opensearchapi/api.cat.aliases.go
+++ b/opensearchapi/api.cat.aliases.go
@@ -180,7 +180,7 @@ func (r CatAliasesRequest) Do(ctx context.Context, transport Transport) (*Respon
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.allocation.go b/opensearchapi/api.cat.allocation.go
index e15739f..ff76b99 100644
--- a/opensearchapi/api.cat.allocation.go
+++ b/opensearchapi/api.cat.allocation.go
@@ -192,7 +192,7 @@ func (r CatAllocationRequest) Do(ctx context.Context, transport Transport) (*Res
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.cluster_manager.go b/opensearchapi/api.cat.cluster_manager.go
index bf8aeaa..1483478 100644
--- a/opensearchapi/api.cat.cluster_manager.go
+++ b/opensearchapi/api.cat.cluster_manager.go
@@ -156,7 +156,7 @@ func (r CatClusterManagerRequest) Do(ctx context.Context, transport Transport) (
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.count.go b/opensearchapi/api.cat.count.go
index f56f6bc..1991e0d 100644
--- a/opensearchapi/api.cat.count.go
+++ b/opensearchapi/api.cat.count.go
@@ -171,7 +171,7 @@ func (r CatCountRequest) Do(ctx context.Context, transport Transport) (*Response
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.fielddata.go b/opensearchapi/api.cat.fielddata.go
index 931c354..897512e 100644
--- a/opensearchapi/api.cat.fielddata.go
+++ b/opensearchapi/api.cat.fielddata.go
@@ -180,7 +180,7 @@ func (r CatFielddataRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.health.go b/opensearchapi/api.cat.health.go
index 3b32976..cadbe67 100644
--- a/opensearchapi/api.cat.health.go
+++ b/opensearchapi/api.cat.health.go
@@ -172,7 +172,7 @@ func (r CatHealthRequest) Do(ctx context.Context, transport Transport) (*Respons
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.help.go b/opensearchapi/api.cat.help.go
index 4663cfd..1489e23 100644
--- a/opensearchapi/api.cat.help.go
+++ b/opensearchapi/api.cat.help.go
@@ -146,7 +146,7 @@ func (r CatHelpRequest) Do(ctx context.Context, transport Transport) (*Response,
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.indices.go b/opensearchapi/api.cat.indices.go
index 5c051b4..71b2c33 100644
--- a/opensearchapi/api.cat.indices.go
+++ b/opensearchapi/api.cat.indices.go
@@ -216,7 +216,7 @@ func (r CatIndicesRequest) Do(ctx context.Context, transport Transport) (*Respon
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.master.go b/opensearchapi/api.cat.master.go
index 9fd681b..cea90de 100644
--- a/opensearchapi/api.cat.master.go
+++ b/opensearchapi/api.cat.master.go
@@ -175,7 +175,7 @@ func (r CatMasterRequest) Do(ctx context.Context, transport Transport) (*Respons
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.nodeattrs.go b/opensearchapi/api.cat.nodeattrs.go
index a6fe516..7468b35 100644
--- a/opensearchapi/api.cat.nodeattrs.go
+++ b/opensearchapi/api.cat.nodeattrs.go
@@ -177,7 +177,7 @@ func (r CatNodeattrsRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.nodes.go b/opensearchapi/api.cat.nodes.go
index dd39e83..90d7703 100644
--- a/opensearchapi/api.cat.nodes.go
+++ b/opensearchapi/api.cat.nodes.go
@@ -197,7 +197,7 @@ func (r CatNodesRequest) Do(ctx context.Context, transport Transport) (*Response
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.pending_tasks.go b/opensearchapi/api.cat.pending_tasks.go
index e662651..992dae7 100644
--- a/opensearchapi/api.cat.pending_tasks.go
+++ b/opensearchapi/api.cat.pending_tasks.go
@@ -182,7 +182,7 @@ func (r CatPendingTasksRequest) Do(ctx context.Context, transport Transport) (*R
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.plugins.go b/opensearchapi/api.cat.plugins.go
index 3805a20..bc8b1e6 100644
--- a/opensearchapi/api.cat.plugins.go
+++ b/opensearchapi/api.cat.plugins.go
@@ -182,7 +182,7 @@ func (r CatPluginsRequest) Do(ctx context.Context, transport Transport) (*Respon
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.recovery.go b/opensearchapi/api.cat.recovery.go
index 1ab1758..5db0841 100644
--- a/opensearchapi/api.cat.recovery.go
+++ b/opensearchapi/api.cat.recovery.go
@@ -194,7 +194,7 @@ func (r CatRecoveryRequest) Do(ctx context.Context, transport Transport) (*Respo
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.repositories.go b/opensearchapi/api.cat.repositories.go
index d249335..c7057a9 100644
--- a/opensearchapi/api.cat.repositories.go
+++ b/opensearchapi/api.cat.repositories.go
@@ -177,7 +177,7 @@ func (r CatRepositoriesRequest) Do(ctx context.Context, transport Transport) (*R
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.segments.go b/opensearchapi/api.cat.segments.go
index 1ee4ab8..aed566e 100644
--- a/opensearchapi/api.cat.segments.go
+++ b/opensearchapi/api.cat.segments.go
@@ -175,7 +175,7 @@ func (r CatSegmentsRequest) Do(ctx context.Context, transport Transport) (*Respo
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.shards.go b/opensearchapi/api.cat.shards.go
index 2320451..9d65493 100644
--- a/opensearchapi/api.cat.shards.go
+++ b/opensearchapi/api.cat.shards.go
@@ -196,7 +196,7 @@ func (r CatShardsRequest) Do(ctx context.Context, transport Transport) (*Respons
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.snapshots.go b/opensearchapi/api.cat.snapshots.go
index 08d680a..1c8e402 100644
--- a/opensearchapi/api.cat.snapshots.go
+++ b/opensearchapi/api.cat.snapshots.go
@@ -191,7 +191,7 @@ func (r CatSnapshotsRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.tasks.go b/opensearchapi/api.cat.tasks.go
index 32412b7..3592808 100644
--- a/opensearchapi/api.cat.tasks.go
+++ b/opensearchapi/api.cat.tasks.go
@@ -186,7 +186,7 @@ func (r CatTasksRequest) Do(ctx context.Context, transport Transport) (*Response
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.templates.go b/opensearchapi/api.cat.templates.go
index 0f0f75e..c4d343e 100644
--- a/opensearchapi/api.cat.templates.go
+++ b/opensearchapi/api.cat.templates.go
@@ -186,7 +186,7 @@ func (r CatTemplatesRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cat.thread_pool.go b/opensearchapi/api.cat.thread_pool.go
index a3e572f..fb1ea0d 100644
--- a/opensearchapi/api.cat.thread_pool.go
+++ b/opensearchapi/api.cat.thread_pool.go
@@ -192,7 +192,7 @@ func (r CatThreadPoolRequest) Do(ctx context.Context, transport Transport) (*Res
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.clear_scroll.go b/opensearchapi/api.clear_scroll.go
index c205171..4fe1660 100644
--- a/opensearchapi/api.clear_scroll.go
+++ b/opensearchapi/api.clear_scroll.go
@@ -150,7 +150,7 @@ func (r ClearScrollRequest) Do(ctx context.Context, transport Transport) (*Respo
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.allocation_explain.go b/opensearchapi/api.cluster.allocation_explain.go
index 7da7ce7..089f13b 100644
--- a/opensearchapi/api.cluster.allocation_explain.go
+++ b/opensearchapi/api.cluster.allocation_explain.go
@@ -153,7 +153,7 @@ func (r ClusterAllocationExplainRequest) Do(ctx context.Context, transport Trans
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.delete_component_template.go b/opensearchapi/api.cluster.delete_component_template.go
index f7e8d38..4078486 100644
--- a/opensearchapi/api.cluster.delete_component_template.go
+++ b/opensearchapi/api.cluster.delete_component_template.go
@@ -156,7 +156,7 @@ func (r ClusterDeleteComponentTemplateRequest) Do(ctx context.Context, transport
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.delete_voting_config_exclusions.go b/opensearchapi/api.cluster.delete_voting_config_exclusions.go
index 046b080..dcc7d88 100644
--- a/opensearchapi/api.cluster.delete_voting_config_exclusions.go
+++ b/opensearchapi/api.cluster.delete_voting_config_exclusions.go
@@ -141,7 +141,7 @@ func (r ClusterDeleteVotingConfigExclusionsRequest) Do(ctx context.Context, tran
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.exists_component_template.go b/opensearchapi/api.cluster.exists_component_template.go
index 36c951c..d1fb93b 100644
--- a/opensearchapi/api.cluster.exists_component_template.go
+++ b/opensearchapi/api.cluster.exists_component_template.go
@@ -157,7 +157,7 @@ func (r ClusterExistsComponentTemplateRequest) Do(ctx context.Context, transport
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.get_component_template.go b/opensearchapi/api.cluster.get_component_template.go
index 9e97ce8..b883b8f 100644
--- a/opensearchapi/api.cluster.get_component_template.go
+++ b/opensearchapi/api.cluster.get_component_template.go
@@ -159,7 +159,7 @@ func (r ClusterGetComponentTemplateRequest) Do(ctx context.Context, transport Tr
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.get_settings.go b/opensearchapi/api.cluster.get_settings.go
index bdf565f..fd0608f 100644
--- a/opensearchapi/api.cluster.get_settings.go
+++ b/opensearchapi/api.cluster.get_settings.go
@@ -162,7 +162,7 @@ func (r ClusterGetSettingsRequest) Do(ctx context.Context, transport Transport)
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.health.go b/opensearchapi/api.cluster.health.go
index 4cdc833..05869c5 100644
--- a/opensearchapi/api.cluster.health.go
+++ b/opensearchapi/api.cluster.health.go
@@ -206,7 +206,7 @@ func (r ClusterHealthRequest) Do(ctx context.Context, transport Transport) (*Res
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.pending_tasks.go b/opensearchapi/api.cluster.pending_tasks.go
index 9039cc2..4004065 100644
--- a/opensearchapi/api.cluster.pending_tasks.go
+++ b/opensearchapi/api.cluster.pending_tasks.go
@@ -153,7 +153,7 @@ func (r ClusterPendingTasksRequest) Do(ctx context.Context, transport Transport)
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.post_voting_config_exclusions.go b/opensearchapi/api.cluster.post_voting_config_exclusions.go
index 63692fc..dcc341b 100644
--- a/opensearchapi/api.cluster.post_voting_config_exclusions.go
+++ b/opensearchapi/api.cluster.post_voting_config_exclusions.go
@@ -151,7 +151,7 @@ func (r ClusterPostVotingConfigExclusionsRequest) Do(ctx context.Context, transp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.put_component_template.go b/opensearchapi/api.cluster.put_component_template.go
index ac12663..1b003d6 100644
--- a/opensearchapi/api.cluster.put_component_template.go
+++ b/opensearchapi/api.cluster.put_component_template.go
@@ -169,7 +169,7 @@ func (r ClusterPutComponentTemplateRequest) Do(ctx context.Context, transport Tr
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.put_settings.go b/opensearchapi/api.cluster.put_settings.go
index a898de3..ed01b6f 100644
--- a/opensearchapi/api.cluster.put_settings.go
+++ b/opensearchapi/api.cluster.put_settings.go
@@ -164,7 +164,7 @@ func (r ClusterPutSettingsRequest) Do(ctx context.Context, transport Transport)
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.remote_info.go b/opensearchapi/api.cluster.remote_info.go
index c35166b..5e1fc95 100644
--- a/opensearchapi/api.cluster.remote_info.go
+++ b/opensearchapi/api.cluster.remote_info.go
@@ -134,7 +134,7 @@ func (r ClusterRemoteInfoRequest) Do(ctx context.Context, transport Transport) (
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.reroute.go b/opensearchapi/api.cluster.reroute.go
index a5d0be0..a0f3d1b 100644
--- a/opensearchapi/api.cluster.reroute.go
+++ b/opensearchapi/api.cluster.reroute.go
@@ -179,7 +179,7 @@ func (r ClusterRerouteRequest) Do(ctx context.Context, transport Transport) (*Re
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.state.go b/opensearchapi/api.cluster.state.go
index 211c39e..520a901 100644
--- a/opensearchapi/api.cluster.state.go
+++ b/opensearchapi/api.cluster.state.go
@@ -197,7 +197,7 @@ func (r ClusterStateRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.cluster.stats.go b/opensearchapi/api.cluster.stats.go
index a400678..f76d38f 100644
--- a/opensearchapi/api.cluster.stats.go
+++ b/opensearchapi/api.cluster.stats.go
@@ -158,7 +158,7 @@ func (r ClusterStatsRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.count.go b/opensearchapi/api.count.go
index 8f17475..b38aff0 100644
--- a/opensearchapi/api.count.go
+++ b/opensearchapi/api.count.go
@@ -220,7 +220,7 @@ func (r CountRequest) Do(ctx context.Context, transport Transport) (*Response, e
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.create.go b/opensearchapi/api.create.go
index c247307..f86efa9 100644
--- a/opensearchapi/api.create.go
+++ b/opensearchapi/api.create.go
@@ -189,7 +189,7 @@ func (r CreateRequest) Do(ctx context.Context, transport Transport) (*Response,
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.dangling_indices.delete_dangling_index.go b/opensearchapi/api.dangling_indices.delete_dangling_index.go
index 33c4588..c5c5ee1 100644
--- a/opensearchapi/api.dangling_indices.delete_dangling_index.go
+++ b/opensearchapi/api.dangling_indices.delete_dangling_index.go
@@ -162,7 +162,7 @@ func (r DanglingIndicesDeleteDanglingIndexRequest) Do(ctx context.Context, trans
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.dangling_indices.import_dangling_index.go b/opensearchapi/api.dangling_indices.import_dangling_index.go
index e251b43..a7cba7d 100644
--- a/opensearchapi/api.dangling_indices.import_dangling_index.go
+++ b/opensearchapi/api.dangling_indices.import_dangling_index.go
@@ -162,7 +162,7 @@ func (r DanglingIndicesImportDanglingIndexRequest) Do(ctx context.Context, trans
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.dangling_indices.list_dangling_indices.go b/opensearchapi/api.dangling_indices.list_dangling_indices.go
index a452377..f54af68 100644
--- a/opensearchapi/api.dangling_indices.list_dangling_indices.go
+++ b/opensearchapi/api.dangling_indices.list_dangling_indices.go
@@ -134,7 +134,7 @@ func (r DanglingIndicesListDanglingIndicesRequest) Do(ctx context.Context, trans
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.delete.go b/opensearchapi/api.delete.go
index f3a9040..f264453 100644
--- a/opensearchapi/api.delete.go
+++ b/opensearchapi/api.delete.go
@@ -184,7 +184,7 @@ func (r DeleteRequest) Do(ctx context.Context, transport Transport) (*Response,
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.delete_by_query.go b/opensearchapi/api.delete_by_query.go
index 371378a..0e9509b 100644
--- a/opensearchapi/api.delete_by_query.go
+++ b/opensearchapi/api.delete_by_query.go
@@ -315,7 +315,7 @@ func (r DeleteByQueryRequest) Do(ctx context.Context, transport Transport) (*Res
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.delete_by_query_rethrottle.go b/opensearchapi/api.delete_by_query_rethrottle.go
index befdf5a..63f9886 100644
--- a/opensearchapi/api.delete_by_query_rethrottle.go
+++ b/opensearchapi/api.delete_by_query_rethrottle.go
@@ -148,7 +148,7 @@ func (r DeleteByQueryRethrottleRequest) Do(ctx context.Context, transport Transp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.delete_script.go b/opensearchapi/api.delete_script.go
index 3f8e385..af1a031 100644
--- a/opensearchapi/api.delete_script.go
+++ b/opensearchapi/api.delete_script.go
@@ -156,7 +156,7 @@ func (r DeleteScriptRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.exists.go b/opensearchapi/api.exists.go
index ab53f42..7f2eb1b 100644
--- a/opensearchapi/api.exists.go
+++ b/opensearchapi/api.exists.go
@@ -193,7 +193,7 @@ func (r ExistsRequest) Do(ctx context.Context, transport Transport) (*Response,
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.exists_source.go b/opensearchapi/api.exists_source.go
index d154faf..0ae58dc 100644
--- a/opensearchapi/api.exists_source.go
+++ b/opensearchapi/api.exists_source.go
@@ -189,7 +189,7 @@ func (r ExistsSourceRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.explain.go b/opensearchapi/api.explain.go
index ea80dea..0d7440b 100644
--- a/opensearchapi/api.explain.go
+++ b/opensearchapi/api.explain.go
@@ -211,7 +211,7 @@ func (r ExplainRequest) Do(ctx context.Context, transport Transport) (*Response,
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.field_caps.go b/opensearchapi/api.field_caps.go
index 5dfedc8..2ab0d86 100644
--- a/opensearchapi/api.field_caps.go
+++ b/opensearchapi/api.field_caps.go
@@ -175,7 +175,7 @@ func (r FieldCapsRequest) Do(ctx context.Context, transport Transport) (*Respons
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.get.go b/opensearchapi/api.get.go
index 36612f9..4f3ba38 100644
--- a/opensearchapi/api.get.go
+++ b/opensearchapi/api.get.go
@@ -193,7 +193,7 @@ func (r GetRequest) Do(ctx context.Context, transport Transport) (*Response, err
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.get_script.go b/opensearchapi/api.get_script.go
index 9a8be7f..759a90c 100644
--- a/opensearchapi/api.get_script.go
+++ b/opensearchapi/api.get_script.go
@@ -151,7 +151,7 @@ func (r GetScriptRequest) Do(ctx context.Context, transport Transport) (*Respons
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.get_script_context.go b/opensearchapi/api.get_script_context.go
index 769a3d0..6b04e18 100644
--- a/opensearchapi/api.get_script_context.go
+++ b/opensearchapi/api.get_script_context.go
@@ -136,7 +136,7 @@ func (r GetScriptContextRequest) Do(ctx context.Context, transport Transport) (*
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.get_script_languages.go b/opensearchapi/api.get_script_languages.go
index 647bc8e..919beaf 100644
--- a/opensearchapi/api.get_script_languages.go
+++ b/opensearchapi/api.get_script_languages.go
@@ -136,7 +136,7 @@ func (r GetScriptLanguagesRequest) Do(ctx context.Context, transport Transport)
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.get_source.go b/opensearchapi/api.get_source.go
index 9a7dc12..42341db 100644
--- a/opensearchapi/api.get_source.go
+++ b/opensearchapi/api.get_source.go
@@ -189,7 +189,7 @@ func (r GetSourceRequest) Do(ctx context.Context, transport Transport) (*Respons
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.index.go b/opensearchapi/api.index.go
index 2f7c049..b4044ee 100644
--- a/opensearchapi/api.index.go
+++ b/opensearchapi/api.index.go
@@ -212,7 +212,7 @@ func (r IndexRequest) Do(ctx context.Context, transport Transport) (*Response, e
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.add_block.go b/opensearchapi/api.indices.add_block.go
index 59ee03c..5a8705b 100644
--- a/opensearchapi/api.indices.add_block.go
+++ b/opensearchapi/api.indices.add_block.go
@@ -176,7 +176,7 @@ func (r IndicesAddBlockRequest) Do(ctx context.Context, transport Transport) (*R
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.analyze.go b/opensearchapi/api.indices.analyze.go
index e3f039d..cd8029d 100644
--- a/opensearchapi/api.indices.analyze.go
+++ b/opensearchapi/api.indices.analyze.go
@@ -152,7 +152,7 @@ func (r IndicesAnalyzeRequest) Do(ctx context.Context, transport Transport) (*Re
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.clear_cache.go b/opensearchapi/api.indices.clear_cache.go
index f3ce8ce..dd62c87 100644
--- a/opensearchapi/api.indices.clear_cache.go
+++ b/opensearchapi/api.indices.clear_cache.go
@@ -184,7 +184,7 @@ func (r IndicesClearCacheRequest) Do(ctx context.Context, transport Transport) (
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.clone.go b/opensearchapi/api.indices.clone.go
index ce98843..2c1bce0 100644
--- a/opensearchapi/api.indices.clone.go
+++ b/opensearchapi/api.indices.clone.go
@@ -172,7 +172,7 @@ func (r IndicesCloneRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.close.go b/opensearchapi/api.indices.close.go
index 830b104..9ed4e2a 100644
--- a/opensearchapi/api.indices.close.go
+++ b/opensearchapi/api.indices.close.go
@@ -177,7 +177,7 @@ func (r IndicesCloseRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.create.go b/opensearchapi/api.indices.create.go
index 93b4aac..bee47ff 100644
--- a/opensearchapi/api.indices.create.go
+++ b/opensearchapi/api.indices.create.go
@@ -166,7 +166,7 @@ func (r IndicesCreateRequest) Do(ctx context.Context, transport Transport) (*Res
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.create_datastream.go b/opensearchapi/api.indices.create_datastream.go
new file mode 100644
index 0000000..d7094b3
--- /dev/null
+++ b/opensearchapi/api.indices.create_datastream.go
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+// Modifications Copyright OpenSearch Contributors. See
+// GitHub history for details.
+
+package opensearchapi
+
+import (
+	"context"
+	"net/http"
+	"strings"
+)
+
+func newIndicesCreateDataStreamFunc(t Transport) IndicesCreateDataStream {
+	return func(name string, o ...func(*IndicesCreateDataStreamRequest)) (*Response, error) {
+		var r = IndicesCreateDataStreamRequest{Name: name}
+		for _, f := range o {
+			f(&r)
+		}
+		return r.Do(r.ctx, t)
+	}
+}
+
+// ----- API Definition -------------------------------------------------------
+
+// IndicesCreateDataStream creates a data stream.
+type IndicesCreateDataStream func(index string, o ...func(*IndicesCreateDataStreamRequest)) (*Response, error)
+
+// IndicesCreateDataStreamRequest configures the Indices Create Data Stream API request.
+type IndicesCreateDataStreamRequest struct {
+	Name string
+
+	Pretty     bool
+	Human      bool
+	ErrorTrace bool
+	FilterPath []string
+
+	Header http.Header
+
+	ctx context.Context
+}
+
+// Do execute the request and returns response or error.
+func (r IndicesCreateDataStreamRequest) Do(ctx context.Context, transport Transport) (*Response, error) {
+	var (
+		method string
+		path   strings.Builder
+		params map[string]string
+	)
+
+	method = "PUT"
+
+	path.Grow(1 + len("_data_stream") + 1 + len(r.Name))
+	path.WriteString("/_data_stream/")
+	path.WriteString(r.Name)
+
+	params = make(map[string]string)
+
+	if r.Pretty {
+		params["pretty"] = "true"
+	}
+
+	if r.Human {
+		params["human"] = "true"
+	}
+
+	if r.ErrorTrace {
+		params["error_trace"] = "true"
+	}
+
+	if len(r.FilterPath) > 0 {
+		params["filter_path"] = strings.Join(r.FilterPath, ",")
+	}
+
+	req, err := newRequest(method, path.String(), nil)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(params) > 0 {
+		q := req.URL.Query()
+		for k, v := range params {
+			q.Set(k, v)
+		}
+		req.URL.RawQuery = q.Encode()
+	}
+
+	if len(r.Header) > 0 {
+		if len(req.Header) == 0 {
+			req.Header = r.Header
+		} else {
+			for k, vv := range r.Header {
+				for _, v := range vv {
+					req.Header.Add(k, v)
+				}
+			}
+		}
+	}
+
+	if ctx != nil {
+		req = req.WithContext(ctx)
+	}
+
+	res, err := transport.Perform(req)
+	if err != nil {
+		return nil, err
+	}
+
+	response := Response{
+		StatusCode: res.StatusCode,
+		Body:       res.Body,
+		Header:     res.Header,
+	}
+
+	return &response, nil
+}
+
+// WithContext sets the request context.
+func (f IndicesCreateDataStream) WithContext(v context.Context) func(*IndicesCreateDataStreamRequest) {
+	return func(r *IndicesCreateDataStreamRequest) {
+		r.ctx = v
+	}
+}
+
+// WithPretty makes the response body pretty-printed.
+func (f IndicesCreateDataStream) WithPretty() func(*IndicesCreateDataStreamRequest) {
+	return func(r *IndicesCreateDataStreamRequest) {
+		r.Pretty = true
+	}
+}
+
+// WithHuman makes statistical values human-readable.
+func (f IndicesCreateDataStream) WithHuman() func(*IndicesCreateDataStreamRequest) {
+	return func(r *IndicesCreateDataStreamRequest) {
+		r.Human = true
+	}
+}
+
+// WithErrorTrace includes the stack trace for errors in the response body.
+func (f IndicesCreateDataStream) WithErrorTrace() func(*IndicesCreateDataStreamRequest) {
+	return func(r *IndicesCreateDataStreamRequest) {
+		r.ErrorTrace = true
+	}
+}
+
+// WithFilterPath filters the properties of the response body.
+func (f IndicesCreateDataStream) WithFilterPath(v ...string) func(*IndicesCreateDataStreamRequest) {
+	return func(r *IndicesCreateDataStreamRequest) {
+		r.FilterPath = v
+	}
+}
+
+// WithHeader adds the headers to the HTTP request.
+func (f IndicesCreateDataStream) WithHeader(h map[string]string) func(*IndicesCreateDataStreamRequest) {
+	return func(r *IndicesCreateDataStreamRequest) {
+		if r.Header == nil {
+			r.Header = make(http.Header)
+		}
+		for k, v := range h {
+			r.Header.Add(k, v)
+		}
+	}
+}
+
+// WithOpaqueID adds the X-Opaque-Id header to the HTTP request.
+func (f IndicesCreateDataStream) WithOpaqueID(s string) func(*IndicesCreateDataStreamRequest) {
+	return func(r *IndicesCreateDataStreamRequest) {
+		if r.Header == nil {
+			r.Header = make(http.Header)
+		}
+		r.Header.Set("X-Opaque-Id", s)
+	}
+}
diff --git a/opensearchapi/api.indices.delete.go b/opensearchapi/api.indices.delete.go
index 92544be..96ffaa6 100644
--- a/opensearchapi/api.indices.delete.go
+++ b/opensearchapi/api.indices.delete.go
@@ -170,7 +170,7 @@ func (r IndicesDeleteRequest) Do(ctx context.Context, transport Transport) (*Res
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.delete_alias.go b/opensearchapi/api.indices.delete_alias.go
index ee8dcd6..069e4e0 100644
--- a/opensearchapi/api.indices.delete_alias.go
+++ b/opensearchapi/api.indices.delete_alias.go
@@ -160,7 +160,7 @@ func (r IndicesDeleteAliasRequest) Do(ctx context.Context, transport Transport)
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.delete_datastream.go b/opensearchapi/api.indices.delete_datastream.go
new file mode 100644
index 0000000..958d46b
--- /dev/null
+++ b/opensearchapi/api.indices.delete_datastream.go
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+// Modifications Copyright OpenSearch Contributors. See
+// GitHub history for details.
+
+package opensearchapi
+
+import (
+	"context"
+	"net/http"
+	"strings"
+	"time"
+)
+
+func newIndicesDeleteDataStreamFunc(t Transport) IndicesDeleteDataStream {
+	return func(name string, o ...func(*IndicesDeleteDataStreamRequest)) (*Response, error) {
+		var r = IndicesDeleteDataStreamRequest{Name: name}
+		for _, f := range o {
+			f(&r)
+		}
+		return r.Do(r.ctx, t)
+	}
+}
+
+// ----- API Definition -------------------------------------------------------
+
+// IndicesDeleteDataStream deletes the backing indices of a data stream and then deletes the data stream itself.
+type IndicesDeleteDataStream func(name string, o ...func(*IndicesDeleteDataStreamRequest)) (*Response, error)
+
+// IndicesDeleteDataStreamRequest configures the Data Stream Delete Template API request.
+type IndicesDeleteDataStreamRequest struct {
+	Name string
+
+	ClusterManagerTimeout time.Duration
+	Timeout               time.Duration
+
+	Pretty     bool
+	Human      bool
+	ErrorTrace bool
+	FilterPath []string
+
+	Header http.Header
+
+	ctx context.Context
+}
+
+// Do execute the request and returns response or error.
+func (r IndicesDeleteDataStreamRequest) Do(ctx context.Context, transport Transport) (*Response, error) {
+	var (
+		method string
+		path   strings.Builder
+		params map[string]string
+	)
+
+	method = "DELETE"
+
+	path.Grow(1 + len("_data_stream") + 1 + len(r.Name))
+	path.WriteString("/_data_stream/")
+	path.WriteString(r.Name)
+
+	params = make(map[string]string)
+
+	if r.ClusterManagerTimeout != 0 {
+		params["cluster_manager_timeout"] = formatDuration(r.ClusterManagerTimeout)
+	}
+
+	if r.Timeout != 0 {
+		params["timeout"] = formatDuration(r.Timeout)
+	}
+
+	if r.Pretty {
+		params["pretty"] = "true"
+	}
+
+	if r.Human {
+		params["human"] = "true"
+	}
+
+	if r.ErrorTrace {
+		params["error_trace"] = "true"
+	}
+
+	if len(r.FilterPath) > 0 {
+		params["filter_path"] = strings.Join(r.FilterPath, ",")
+	}
+
+	req, err := newRequest(method, path.String(), nil)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(params) > 0 {
+		q := req.URL.Query()
+		for k, v := range params {
+			q.Set(k, v)
+		}
+		req.URL.RawQuery = q.Encode()
+	}
+
+	if len(r.Header) > 0 {
+		if len(req.Header) == 0 {
+			req.Header = r.Header
+		} else {
+			for k, vv := range r.Header {
+				for _, v := range vv {
+					req.Header.Add(k, v)
+				}
+			}
+		}
+	}
+
+	if ctx != nil {
+		req = req.WithContext(ctx)
+	}
+
+	res, err := transport.Perform(req)
+	if err != nil {
+		return nil, err
+	}
+
+	response := Response{
+		StatusCode: res.StatusCode,
+		Body:       res.Body,
+		Header:     res.Header,
+	}
+
+	return &response, nil
+}
+
+// WithContext sets the request context.
+func (f IndicesDeleteDataStream) WithContext(v context.Context) func(*IndicesDeleteDataStreamRequest) {
+	return func(r *IndicesDeleteDataStreamRequest) {
+		r.ctx = v
+	}
+}
+
+// WithClusterManagerTimeout - explicit operation timeout for connection to cluster-manager node.
+func (f IndicesDeleteDataStream) WithClusterManagerTimeout(v time.Duration) func(*IndicesDeleteDataStreamRequest) {
+	return func(r *IndicesDeleteDataStreamRequest) {
+		r.ClusterManagerTimeout = v
+	}
+}
+
+// WithTimeout - explicit operation timeout.
+func (f IndicesDeleteDataStream) WithTimeout(v time.Duration) func(*IndicesDeleteDataStreamRequest) {
+	return func(r *IndicesDeleteDataStreamRequest) {
+		r.Timeout = v
+	}
+}
+
+// WithPretty makes the response body pretty-printed.
+func (f IndicesDeleteDataStream) WithPretty() func(*IndicesDeleteDataStreamRequest) {
+	return func(r *IndicesDeleteDataStreamRequest) {
+		r.Pretty = true
+	}
+}
+
+// WithHuman makes statistical values human-readable.
+func (f IndicesDeleteDataStream) WithHuman() func(*IndicesDeleteDataStreamRequest) {
+	return func(r *IndicesDeleteDataStreamRequest) {
+		r.Human = true
+	}
+}
+
+// WithErrorTrace includes the stack trace for errors in the response body.
+func (f IndicesDeleteDataStream) WithErrorTrace() func(*IndicesDeleteDataStreamRequest) {
+	return func(r *IndicesDeleteDataStreamRequest) {
+		r.ErrorTrace = true
+	}
+}
+
+// WithFilterPath filters the properties of the response body.
+func (f IndicesDeleteDataStream) WithFilterPath(v ...string) func(*IndicesDeleteDataStreamRequest) {
+	return func(r *IndicesDeleteDataStreamRequest) {
+		r.FilterPath = v
+	}
+}
+
+// WithHeader adds the headers to the HTTP request.
+func (f IndicesDeleteDataStream) WithHeader(h map[string]string) func(*IndicesDeleteDataStreamRequest) {
+	return func(r *IndicesDeleteDataStreamRequest) {
+		if r.Header == nil {
+			r.Header = make(http.Header)
+		}
+		for k, v := range h {
+			r.Header.Add(k, v)
+		}
+	}
+}
+
+// WithOpaqueID adds the X-Opaque-Id header to the HTTP request.
+func (f IndicesDeleteDataStream) WithOpaqueID(s string) func(*IndicesDeleteDataStreamRequest) {
+	return func(r *IndicesDeleteDataStreamRequest) {
+		if r.Header == nil {
+			r.Header = make(http.Header)
+		}
+		r.Header.Set("X-Opaque-Id", s)
+	}
+}
diff --git a/opensearchapi/api.indices.delete_index_template.go b/opensearchapi/api.indices.delete_index_template.go
index 777e101..82a60a0 100644
--- a/opensearchapi/api.indices.delete_index_template.go
+++ b/opensearchapi/api.indices.delete_index_template.go
@@ -156,7 +156,7 @@ func (r IndicesDeleteIndexTemplateRequest) Do(ctx context.Context, transport Tra
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.delete_template.go b/opensearchapi/api.indices.delete_template.go
index 7a9b2ca..f789c9a 100644
--- a/opensearchapi/api.indices.delete_template.go
+++ b/opensearchapi/api.indices.delete_template.go
@@ -156,7 +156,7 @@ func (r IndicesDeleteTemplateRequest) Do(ctx context.Context, transport Transpor
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.disk_usage.go b/opensearchapi/api.indices.disk_usage.go
index da3ac56..b3c12c9 100644
--- a/opensearchapi/api.indices.disk_usage.go
+++ b/opensearchapi/api.indices.disk_usage.go
@@ -168,7 +168,7 @@ func (r IndicesDiskUsageRequest) Do(ctx context.Context, transport Transport) (*
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.exists.go b/opensearchapi/api.indices.exists.go
index 70f6d04..fc3fd29 100644
--- a/opensearchapi/api.indices.exists.go
+++ b/opensearchapi/api.indices.exists.go
@@ -169,7 +169,7 @@ func (r IndicesExistsRequest) Do(ctx context.Context, transport Transport) (*Res
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.exists_alias.go b/opensearchapi/api.indices.exists_alias.go
index a878227..6302004 100644
--- a/opensearchapi/api.indices.exists_alias.go
+++ b/opensearchapi/api.indices.exists_alias.go
@@ -167,7 +167,7 @@ func (r IndicesExistsAliasRequest) Do(ctx context.Context, transport Transport)
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.exists_index_template.go b/opensearchapi/api.indices.exists_index_template.go
index 901f0fe..bcc501f 100644
--- a/opensearchapi/api.indices.exists_index_template.go
+++ b/opensearchapi/api.indices.exists_index_template.go
@@ -162,7 +162,7 @@ func (r IndicesExistsIndexTemplateRequest) Do(ctx context.Context, transport Tra
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.exists_template.go b/opensearchapi/api.indices.exists_template.go
index 89e6049..096c977 100644
--- a/opensearchapi/api.indices.exists_template.go
+++ b/opensearchapi/api.indices.exists_template.go
@@ -162,7 +162,7 @@ func (r IndicesExistsTemplateRequest) Do(ctx context.Context, transport Transpor
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.field_usage_stats.go b/opensearchapi/api.indices.field_usage_stats.go
index d4a8130..24577e3 100644
--- a/opensearchapi/api.indices.field_usage_stats.go
+++ b/opensearchapi/api.indices.field_usage_stats.go
@@ -163,7 +163,7 @@ func (r IndicesFieldUsageStatsRequest) Do(ctx context.Context, transport Transpo
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.flush.go b/opensearchapi/api.indices.flush.go
index 347d474..be820c8 100644
--- a/opensearchapi/api.indices.flush.go
+++ b/opensearchapi/api.indices.flush.go
@@ -168,7 +168,7 @@ func (r IndicesFlushRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.forcemerge.go b/opensearchapi/api.indices.forcemerge.go
index 712cb6d..7fcd4ca 100644
--- a/opensearchapi/api.indices.forcemerge.go
+++ b/opensearchapi/api.indices.forcemerge.go
@@ -173,7 +173,7 @@ func (r IndicesForcemergeRequest) Do(ctx context.Context, transport Transport) (
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.get.go b/opensearchapi/api.indices.get.go
index 86b2574..e9819bc 100644
--- a/opensearchapi/api.indices.get.go
+++ b/opensearchapi/api.indices.get.go
@@ -180,7 +180,7 @@ func (r IndicesGetRequest) Do(ctx context.Context, transport Transport) (*Respon
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.get_alias.go b/opensearchapi/api.indices.get_alias.go
index 420b2fa..38d4dcf 100644
--- a/opensearchapi/api.indices.get_alias.go
+++ b/opensearchapi/api.indices.get_alias.go
@@ -169,7 +169,7 @@ func (r IndicesGetAliasRequest) Do(ctx context.Context, transport Transport) (*R
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.get_datastream.go b/opensearchapi/api.indices.get_datastream.go
new file mode 100644
index 0000000..7facf8b
--- /dev/null
+++ b/opensearchapi/api.indices.get_datastream.go
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+// Modifications Copyright OpenSearch Contributors. See
+// GitHub history for details.
+
+package opensearchapi
+
+import (
+	"context"
+	"net/http"
+	"strings"
+	"time"
+)
+
+func newIndicesGetDataStreamFunc(t Transport) IndicesGetDataStream {
+	return func(o ...func(*IndicesGetDataStreamRequest)) (*Response, error) {
+		var r = IndicesGetDataStreamRequest{}
+		for _, f := range o {
+			f(&r)
+		}
+		return r.Do(r.ctx, t)
+	}
+}
+
+// ----- API Definition -------------------------------------------------------
+
+// IndicesGetDataStream returns a data stream specific information if Name parameter is passed. Otherwise, returns all data streams.
+type IndicesGetDataStream func(o ...func(*IndicesGetDataStreamRequest)) (*Response, error)
+
+// IndicesGetDataStreamRequest configures the Indices Get Data Stream API request.
+type IndicesGetDataStreamRequest struct {
+	Name string
+
+	ClusterManagerTimeout time.Duration
+
+	Pretty     bool
+	Human      bool
+	ErrorTrace bool
+	FilterPath []string
+
+	Header http.Header
+
+	ctx context.Context
+}
+
+// Do execute the request and returns response or error.
+func (r IndicesGetDataStreamRequest) Do(ctx context.Context, transport Transport) (*Response, error) {
+	var (
+		method string
+		path   strings.Builder
+		params map[string]string
+	)
+
+	method = "GET"
+
+	path.Grow(1 + len("_data_stream") + 1 + len(r.Name))
+	path.WriteString("/_data_stream/")
+	path.WriteString(r.Name)
+
+	params = make(map[string]string)
+
+	if r.ClusterManagerTimeout != 0 {
+		params["cluster_manager_timeout"] = formatDuration(r.ClusterManagerTimeout)
+	}
+
+	if r.Pretty {
+		params["pretty"] = "true"
+	}
+
+	if r.Human {
+		params["human"] = "true"
+	}
+
+	if r.ErrorTrace {
+		params["error_trace"] = "true"
+	}
+
+	if len(r.FilterPath) > 0 {
+		params["filter_path"] = strings.Join(r.FilterPath, ",")
+	}
+
+	req, err := newRequest(method, path.String(), nil)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(params) > 0 {
+		q := req.URL.Query()
+		for k, v := range params {
+			q.Set(k, v)
+		}
+		req.URL.RawQuery = q.Encode()
+	}
+
+	if len(r.Header) > 0 {
+		if len(req.Header) == 0 {
+			req.Header = r.Header
+		} else {
+			for k, vv := range r.Header {
+				for _, v := range vv {
+					req.Header.Add(k, v)
+				}
+			}
+		}
+	}
+
+	if ctx != nil {
+		req = req.WithContext(ctx)
+	}
+
+	res, err := transport.Perform(req)
+	if err != nil {
+		return nil, err
+	}
+
+	response := Response{
+		StatusCode: res.StatusCode,
+		Body:       res.Body,
+		Header:     res.Header,
+	}
+
+	return &response, nil
+}
+
+// WithContext sets the request context.
+func (f IndicesGetDataStream) WithContext(v context.Context) func(*IndicesGetDataStreamRequest) {
+	return func(r *IndicesGetDataStreamRequest) {
+		r.ctx = v
+	}
+}
+
+// WithName - the comma separated names of the index templates.
+func (f IndicesGetDataStream) WithName(v string) func(*IndicesGetDataStreamRequest) {
+	return func(r *IndicesGetDataStreamRequest) {
+		r.Name = v
+	}
+}
+
+// WithClusterManagerTimeout - explicit operation timeout for connection to cluster-manager node.
+func (f IndicesGetDataStream) WithClusterManagerTimeout(v time.Duration) func(*IndicesGetDataStreamRequest) {
+	return func(r *IndicesGetDataStreamRequest) {
+		r.ClusterManagerTimeout = v
+	}
+}
+
+// WithPretty makes the response body pretty-printed.
+func (f IndicesGetDataStream) WithPretty() func(*IndicesGetDataStreamRequest) {
+	return func(r *IndicesGetDataStreamRequest) {
+		r.Pretty = true
+	}
+}
+
+// WithHuman makes statistical values human-readable.
+func (f IndicesGetDataStream) WithHuman() func(*IndicesGetDataStreamRequest) {
+	return func(r *IndicesGetDataStreamRequest) {
+		r.Human = true
+	}
+}
+
+// WithErrorTrace includes the stack trace for errors in the response body.
+func (f IndicesGetDataStream) WithErrorTrace() func(*IndicesGetDataStreamRequest) {
+	return func(r *IndicesGetDataStreamRequest) {
+		r.ErrorTrace = true
+	}
+}
+
+// WithFilterPath filters the properties of the response body.
+func (f IndicesGetDataStream) WithFilterPath(v ...string) func(*IndicesGetDataStreamRequest) {
+	return func(r *IndicesGetDataStreamRequest) {
+		r.FilterPath = v
+	}
+}
+
+// WithHeader adds the headers to the HTTP request.
+func (f IndicesGetDataStream) WithHeader(h map[string]string) func(*IndicesGetDataStreamRequest) {
+	return func(r *IndicesGetDataStreamRequest) {
+		if r.Header == nil {
+			r.Header = make(http.Header)
+		}
+		for k, v := range h {
+			r.Header.Add(k, v)
+		}
+	}
+}
+
+// WithOpaqueID adds the X-Opaque-Id header to the HTTP request.
+func (f IndicesGetDataStream) WithOpaqueID(s string) func(*IndicesGetDataStreamRequest) {
+	return func(r *IndicesGetDataStreamRequest) {
+		if r.Header == nil {
+			r.Header = make(http.Header)
+		}
+		r.Header.Set("X-Opaque-Id", s)
+	}
+}
diff --git a/opensearchapi/api.indices.get_datastream_stats.go b/opensearchapi/api.indices.get_datastream_stats.go
new file mode 100644
index 0000000..4bc2e93
--- /dev/null
+++ b/opensearchapi/api.indices.get_datastream_stats.go
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+// Modifications Copyright OpenSearch Contributors. See
+// GitHub history for details.
+
+package opensearchapi
+
+import (
+	"context"
+	"net/http"
+	"strings"
+	"time"
+)
+
+func newIndicesGetDataStreamStatsFunc(t Transport) IndicesGetDataStreamStats {
+	return func(o ...func(*IndicesGetDataStreamStatsRequest)) (*Response, error) {
+		var r = IndicesGetDataStreamStatsRequest{}
+		for _, f := range o {
+			f(&r)
+		}
+		return r.Do(r.ctx, t)
+	}
+}
+
+// ----- API Definition -------------------------------------------------------
+
+// IndicesGetDataStreamStats returns a more insights about the data stream.
+type IndicesGetDataStreamStats func(o ...func(*IndicesGetDataStreamStatsRequest)) (*Response, error)
+
+// IndicesGetDataStreamStatsRequest configures the Indices Get Data Stream Stats API request.
+type IndicesGetDataStreamStatsRequest struct {
+	Name string
+
+	ClusterManagerTimeout time.Duration
+
+	Pretty     bool
+	Human      bool
+	ErrorTrace bool
+	FilterPath []string
+
+	Header http.Header
+
+	ctx context.Context
+}
+
+// Do execute the request and returns response or error.
+func (r IndicesGetDataStreamStatsRequest) Do(ctx context.Context, transport Transport) (*Response, error) {
+	var (
+		method string
+		path   strings.Builder
+		params map[string]string
+	)
+
+	method = "GET"
+
+	path.Grow(1 + len("_data_stream") + 1 + len(r.Name) + 1 + len("_stats"))
+	path.WriteString("/_data_stream/")
+	path.WriteString(r.Name)
+	path.WriteString("/_stats")
+
+	params = make(map[string]string)
+
+	if r.ClusterManagerTimeout != 0 {
+		params["cluster_manager_timeout"] = formatDuration(r.ClusterManagerTimeout)
+	}
+
+	if r.Pretty {
+		params["pretty"] = "true"
+	}
+
+	if r.Human {
+		params["human"] = "true"
+	}
+
+	if r.ErrorTrace {
+		params["error_trace"] = "true"
+	}
+
+	if len(r.FilterPath) > 0 {
+		params["filter_path"] = strings.Join(r.FilterPath, ",")
+	}
+
+	req, err := newRequest(method, path.String(), nil)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(params) > 0 {
+		q := req.URL.Query()
+		for k, v := range params {
+			q.Set(k, v)
+		}
+		req.URL.RawQuery = q.Encode()
+	}
+
+	if len(r.Header) > 0 {
+		if len(req.Header) == 0 {
+			req.Header = r.Header
+		} else {
+			for k, vv := range r.Header {
+				for _, v := range vv {
+					req.Header.Add(k, v)
+				}
+			}
+		}
+	}
+
+	if ctx != nil {
+		req = req.WithContext(ctx)
+	}
+
+	res, err := transport.Perform(req)
+	if err != nil {
+		return nil, err
+	}
+
+	response := Response{
+		StatusCode: res.StatusCode,
+		Body:       res.Body,
+		Header:     res.Header,
+	}
+
+	return &response, nil
+}
+
+// WithContext sets the request context.
+func (f IndicesGetDataStreamStats) WithContext(v context.Context) func(*IndicesGetDataStreamStatsRequest) {
+	return func(r *IndicesGetDataStreamStatsRequest) {
+		r.ctx = v
+	}
+}
+
+// WithName - the comma separated names of the index templates.
+func (f IndicesGetDataStreamStats) WithName(v string) func(*IndicesGetDataStreamStatsRequest) {
+	return func(r *IndicesGetDataStreamStatsRequest) {
+		r.Name = v
+	}
+}
+
+// WithClusterManagerTimeout - explicit operation timeout for connection to cluster-manager node.
+func (f IndicesGetDataStreamStats) WithClusterManagerTimeout(v time.Duration) func(*IndicesGetDataStreamStatsRequest) {
+	return func(r *IndicesGetDataStreamStatsRequest) {
+		r.ClusterManagerTimeout = v
+	}
+}
+
+// WithPretty makes the response body pretty-printed.
+func (f IndicesGetDataStreamStats) WithPretty() func(*IndicesGetDataStreamStatsRequest) {
+	return func(r *IndicesGetDataStreamStatsRequest) {
+		r.Pretty = true
+	}
+}
+
+// WithHuman makes statistical values human-readable.
+func (f IndicesGetDataStreamStats) WithHuman() func(*IndicesGetDataStreamStatsRequest) {
+	return func(r *IndicesGetDataStreamStatsRequest) {
+		r.Human = true
+	}
+}
+
+// WithErrorTrace includes the stack trace for errors in the response body.
+func (f IndicesGetDataStreamStats) WithErrorTrace() func(*IndicesGetDataStreamStatsRequest) {
+	return func(r *IndicesGetDataStreamStatsRequest) {
+		r.ErrorTrace = true
+	}
+}
+
+// WithFilterPath filters the properties of the response body.
+func (f IndicesGetDataStreamStats) WithFilterPath(v ...string) func(*IndicesGetDataStreamStatsRequest) {
+	return func(r *IndicesGetDataStreamStatsRequest) {
+		r.FilterPath = v
+	}
+}
+
+// WithHeader adds the headers to the HTTP request.
+func (f IndicesGetDataStreamStats) WithHeader(h map[string]string) func(*IndicesGetDataStreamStatsRequest) {
+	return func(r *IndicesGetDataStreamStatsRequest) {
+		if r.Header == nil {
+			r.Header = make(http.Header)
+		}
+		for k, v := range h {
+			r.Header.Add(k, v)
+		}
+	}
+}
+
+// WithOpaqueID adds the X-Opaque-Id header to the HTTP request.
+func (f IndicesGetDataStreamStats) WithOpaqueID(s string) func(*IndicesGetDataStreamStatsRequest) {
+	return func(r *IndicesGetDataStreamStatsRequest) {
+		if r.Header == nil {
+			r.Header = make(http.Header)
+		}
+		r.Header.Set("X-Opaque-Id", s)
+	}
+}
diff --git a/opensearchapi/api.indices.get_field_mapping.go b/opensearchapi/api.indices.get_field_mapping.go
index 911b519..fb161ea 100644
--- a/opensearchapi/api.indices.get_field_mapping.go
+++ b/opensearchapi/api.indices.get_field_mapping.go
@@ -174,7 +174,7 @@ func (r IndicesGetFieldMappingRequest) Do(ctx context.Context, transport Transpo
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.get_index_template.go b/opensearchapi/api.indices.get_index_template.go
index c17e15e..6ad2168 100644
--- a/opensearchapi/api.indices.get_index_template.go
+++ b/opensearchapi/api.indices.get_index_template.go
@@ -164,7 +164,7 @@ func (r IndicesGetIndexTemplateRequest) Do(ctx context.Context, transport Transp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.get_mapping.go b/opensearchapi/api.indices.get_mapping.go
index c407a8e..716640d 100644
--- a/opensearchapi/api.indices.get_mapping.go
+++ b/opensearchapi/api.indices.get_mapping.go
@@ -173,7 +173,7 @@ func (r IndicesGetMappingRequest) Do(ctx context.Context, transport Transport) (
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.get_settings.go b/opensearchapi/api.indices.get_settings.go
index 816202c..d97b110 100644
--- a/opensearchapi/api.indices.get_settings.go
+++ b/opensearchapi/api.indices.get_settings.go
@@ -190,7 +190,7 @@ func (r IndicesGetSettingsRequest) Do(ctx context.Context, transport Transport)
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.get_template.go b/opensearchapi/api.indices.get_template.go
index 67eb151..a7d3862 100644
--- a/opensearchapi/api.indices.get_template.go
+++ b/opensearchapi/api.indices.get_template.go
@@ -164,7 +164,7 @@ func (r IndicesGetTemplateRequest) Do(ctx context.Context, transport Transport)
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.get_upgrade.go b/opensearchapi/api.indices.get_upgrade.go
index b18843d..04fb222 100644
--- a/opensearchapi/api.indices.get_upgrade.go
+++ b/opensearchapi/api.indices.get_upgrade.go
@@ -158,7 +158,7 @@ func (r IndicesGetUpgradeRequest) Do(ctx context.Context, transport Transport) (
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.open.go b/opensearchapi/api.indices.open.go
index a020f3e..e444d34 100644
--- a/opensearchapi/api.indices.open.go
+++ b/opensearchapi/api.indices.open.go
@@ -177,7 +177,7 @@ func (r IndicesOpenRequest) Do(ctx context.Context, transport Transport) (*Respo
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.put_alias.go b/opensearchapi/api.indices.put_alias.go
index bf0a9a0..f0504be 100644
--- a/opensearchapi/api.indices.put_alias.go
+++ b/opensearchapi/api.indices.put_alias.go
@@ -167,7 +167,7 @@ func (r IndicesPutAliasRequest) Do(ctx context.Context, transport Transport) (*R
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.put_index_template.go b/opensearchapi/api.indices.put_index_template.go
index e34c6c7..2ce8837 100644
--- a/opensearchapi/api.indices.put_index_template.go
+++ b/opensearchapi/api.indices.put_index_template.go
@@ -169,7 +169,7 @@ func (r IndicesPutIndexTemplateRequest) Do(ctx context.Context, transport Transp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.put_mapping.go b/opensearchapi/api.indices.put_mapping.go
index e7d721c..d1f09c3 100644
--- a/opensearchapi/api.indices.put_mapping.go
+++ b/opensearchapi/api.indices.put_mapping.go
@@ -186,7 +186,7 @@ func (r IndicesPutMappingRequest) Do(ctx context.Context, transport Transport) (
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.put_settings.go b/opensearchapi/api.indices.put_settings.go
index 711dd61..370fa31 100644
--- a/opensearchapi/api.indices.put_settings.go
+++ b/opensearchapi/api.indices.put_settings.go
@@ -191,7 +191,7 @@ func (r IndicesPutSettingsRequest) Do(ctx context.Context, transport Transport)
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.put_template.go b/opensearchapi/api.indices.put_template.go
index 34a3a6b..2329a34 100644
--- a/opensearchapi/api.indices.put_template.go
+++ b/opensearchapi/api.indices.put_template.go
@@ -169,7 +169,7 @@ func (r IndicesPutTemplateRequest) Do(ctx context.Context, transport Transport)
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.recovery.go b/opensearchapi/api.indices.recovery.go
index af634d6..c0e636a 100644
--- a/opensearchapi/api.indices.recovery.go
+++ b/opensearchapi/api.indices.recovery.go
@@ -153,7 +153,7 @@ func (r IndicesRecoveryRequest) Do(ctx context.Context, transport Transport) (*R
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.refresh.go b/opensearchapi/api.indices.refresh.go
index f666309..4cd2dc6 100644
--- a/opensearchapi/api.indices.refresh.go
+++ b/opensearchapi/api.indices.refresh.go
@@ -158,7 +158,7 @@ func (r IndicesRefreshRequest) Do(ctx context.Context, transport Transport) (*Re
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.resolve_index.go b/opensearchapi/api.indices.resolve_index.go
index 1411d98..2678e0f 100644
--- a/opensearchapi/api.indices.resolve_index.go
+++ b/opensearchapi/api.indices.resolve_index.go
@@ -149,7 +149,7 @@ func (r IndicesResolveIndexRequest) Do(ctx context.Context, transport Transport)
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.rollover.go b/opensearchapi/api.indices.rollover.go
index 4c3f8c3..3b291b4 100644
--- a/opensearchapi/api.indices.rollover.go
+++ b/opensearchapi/api.indices.rollover.go
@@ -180,7 +180,7 @@ func (r IndicesRolloverRequest) Do(ctx context.Context, transport Transport) (*R
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.segments.go b/opensearchapi/api.indices.segments.go
index 60367e3..0ac2723 100644
--- a/opensearchapi/api.indices.segments.go
+++ b/opensearchapi/api.indices.segments.go
@@ -163,7 +163,7 @@ func (r IndicesSegmentsRequest) Do(ctx context.Context, transport Transport) (*R
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.shard_stores.go b/opensearchapi/api.indices.shard_stores.go
index 181c0bc..8e6bb57 100644
--- a/opensearchapi/api.indices.shard_stores.go
+++ b/opensearchapi/api.indices.shard_stores.go
@@ -163,7 +163,7 @@ func (r IndicesShardStoresRequest) Do(ctx context.Context, transport Transport)
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.shrink.go b/opensearchapi/api.indices.shrink.go
index 11763b3..685cf6b 100644
--- a/opensearchapi/api.indices.shrink.go
+++ b/opensearchapi/api.indices.shrink.go
@@ -178,7 +178,7 @@ func (r IndicesShrinkRequest) Do(ctx context.Context, transport Transport) (*Res
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.simulate_index_template.go b/opensearchapi/api.indices.simulate_index_template.go
index 770f1d9..2912ffd 100644
--- a/opensearchapi/api.indices.simulate_index_template.go
+++ b/opensearchapi/api.indices.simulate_index_template.go
@@ -171,7 +171,7 @@ func (r IndicesSimulateIndexTemplateRequest) Do(ctx context.Context, transport T
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.simulate_template.go b/opensearchapi/api.indices.simulate_template.go
index 9808f2c..aeb5738 100644
--- a/opensearchapi/api.indices.simulate_template.go
+++ b/opensearchapi/api.indices.simulate_template.go
@@ -173,7 +173,7 @@ func (r IndicesSimulateTemplateRequest) Do(ctx context.Context, transport Transp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.split.go b/opensearchapi/api.indices.split.go
index af2c351..49ea6c5 100644
--- a/opensearchapi/api.indices.split.go
+++ b/opensearchapi/api.indices.split.go
@@ -178,7 +178,7 @@ func (r IndicesSplitRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.stats.go b/opensearchapi/api.indices.stats.go
index 38065bd..27b55d5 100644
--- a/opensearchapi/api.indices.stats.go
+++ b/opensearchapi/api.indices.stats.go
@@ -199,7 +199,7 @@ func (r IndicesStatsRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.update_aliases.go b/opensearchapi/api.indices.update_aliases.go
index cc6cd34..9f3bb77 100644
--- a/opensearchapi/api.indices.update_aliases.go
+++ b/opensearchapi/api.indices.update_aliases.go
@@ -158,7 +158,7 @@ func (r IndicesUpdateAliasesRequest) Do(ctx context.Context, transport Transport
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.upgrade.go b/opensearchapi/api.indices.upgrade.go
index bf8550a..e505b6f 100644
--- a/opensearchapi/api.indices.upgrade.go
+++ b/opensearchapi/api.indices.upgrade.go
@@ -168,7 +168,7 @@ func (r IndicesUpgradeRequest) Do(ctx context.Context, transport Transport) (*Re
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.indices.validate_query.go b/opensearchapi/api.indices.validate_query.go
index 8550dcd..db19a59 100644
--- a/opensearchapi/api.indices.validate_query.go
+++ b/opensearchapi/api.indices.validate_query.go
@@ -212,7 +212,7 @@ func (r IndicesValidateQueryRequest) Do(ctx context.Context, transport Transport
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.info.go b/opensearchapi/api.info.go
index c506a52..77bb5e1 100644
--- a/opensearchapi/api.info.go
+++ b/opensearchapi/api.info.go
@@ -134,7 +134,7 @@ func (r InfoRequest) Do(ctx context.Context, transport Transport) (*Response, er
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.ingest.delete_pipeline.go b/opensearchapi/api.ingest.delete_pipeline.go
index beeebe3..1e8dd1e 100644
--- a/opensearchapi/api.ingest.delete_pipeline.go
+++ b/opensearchapi/api.ingest.delete_pipeline.go
@@ -158,7 +158,7 @@ func (r IngestDeletePipelineRequest) Do(ctx context.Context, transport Transport
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.ingest.get_pipeline.go b/opensearchapi/api.ingest.get_pipeline.go
index b0f3bae..bbfd417 100644
--- a/opensearchapi/api.ingest.get_pipeline.go
+++ b/opensearchapi/api.ingest.get_pipeline.go
@@ -161,7 +161,7 @@ func (r IngestGetPipelineRequest) Do(ctx context.Context, transport Transport) (
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.ingest.processor_grok.go b/opensearchapi/api.ingest.processor_grok.go
index 978dbee..af506ab 100644
--- a/opensearchapi/api.ingest.processor_grok.go
+++ b/opensearchapi/api.ingest.processor_grok.go
@@ -134,7 +134,7 @@ func (r IngestProcessorGrokRequest) Do(ctx context.Context, transport Transport)
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.ingest.put_pipeline.go b/opensearchapi/api.ingest.put_pipeline.go
index d8343f3..5a0bea0 100644
--- a/opensearchapi/api.ingest.put_pipeline.go
+++ b/opensearchapi/api.ingest.put_pipeline.go
@@ -165,7 +165,7 @@ func (r IngestPutPipelineRequest) Do(ctx context.Context, transport Transport) (
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.ingest.simulate.go b/opensearchapi/api.ingest.simulate.go
index 682b8d3..926d019 100644
--- a/opensearchapi/api.ingest.simulate.go
+++ b/opensearchapi/api.ingest.simulate.go
@@ -159,7 +159,7 @@ func (r IngestSimulateRequest) Do(ctx context.Context, transport Transport) (*Re
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.mget.go b/opensearchapi/api.mget.go
index 4d38484..6555a65 100644
--- a/opensearchapi/api.mget.go
+++ b/opensearchapi/api.mget.go
@@ -190,7 +190,7 @@ func (r MgetRequest) Do(ctx context.Context, transport Transport) (*Response, er
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.msearch.go b/opensearchapi/api.msearch.go
index 9ad3bfa..edbf7e3 100644
--- a/opensearchapi/api.msearch.go
+++ b/opensearchapi/api.msearch.go
@@ -185,7 +185,7 @@ func (r MsearchRequest) Do(ctx context.Context, transport Transport) (*Response,
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.msearch_template.go b/opensearchapi/api.msearch_template.go
index 51a0913..ea73c4e 100644
--- a/opensearchapi/api.msearch_template.go
+++ b/opensearchapi/api.msearch_template.go
@@ -177,7 +177,7 @@ func (r MsearchTemplateRequest) Do(ctx context.Context, transport Transport) (*R
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.mtermvectors.go b/opensearchapi/api.mtermvectors.go
index ba92431..181d01f 100644
--- a/opensearchapi/api.mtermvectors.go
+++ b/opensearchapi/api.mtermvectors.go
@@ -210,7 +210,7 @@ func (r MtermvectorsRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.nodes.hot_threads.go b/opensearchapi/api.nodes.hot_threads.go
index d9aaee6..d70906e 100644
--- a/opensearchapi/api.nodes.hot_threads.go
+++ b/opensearchapi/api.nodes.hot_threads.go
@@ -173,7 +173,7 @@ func (r NodesHotThreadsRequest) Do(ctx context.Context, transport Transport) (*R
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.nodes.info.go b/opensearchapi/api.nodes.info.go
index 39b9297..02f6043 100644
--- a/opensearchapi/api.nodes.info.go
+++ b/opensearchapi/api.nodes.info.go
@@ -159,7 +159,7 @@ func (r NodesInfoRequest) Do(ctx context.Context, transport Transport) (*Respons
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.nodes.reload_secure_settings.go b/opensearchapi/api.nodes.reload_secure_settings.go
index 9030f02..e210ba0 100644
--- a/opensearchapi/api.nodes.reload_secure_settings.go
+++ b/opensearchapi/api.nodes.reload_secure_settings.go
@@ -157,7 +157,7 @@ func (r NodesReloadSecureSettingsRequest) Do(ctx context.Context, transport Tran
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.nodes.stats.go b/opensearchapi/api.nodes.stats.go
index 6021814..80899e1 100644
--- a/opensearchapi/api.nodes.stats.go
+++ b/opensearchapi/api.nodes.stats.go
@@ -201,7 +201,7 @@ func (r NodesStatsRequest) Do(ctx context.Context, transport Transport) (*Respon
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.nodes.usage.go b/opensearchapi/api.nodes.usage.go
index 25db932..0d2d896 100644
--- a/opensearchapi/api.nodes.usage.go
+++ b/opensearchapi/api.nodes.usage.go
@@ -155,7 +155,7 @@ func (r NodesUsageRequest) Do(ctx context.Context, transport Transport) (*Respon
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.ping.go b/opensearchapi/api.ping.go
index f30ef61..b80ed16 100644
--- a/opensearchapi/api.ping.go
+++ b/opensearchapi/api.ping.go
@@ -134,7 +134,7 @@ func (r PingRequest) Do(ctx context.Context, transport Transport) (*Response, er
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.put_script.go b/opensearchapi/api.put_script.go
index 48a1f67..c8dea2d 100644
--- a/opensearchapi/api.put_script.go
+++ b/opensearchapi/api.put_script.go
@@ -173,7 +173,7 @@ func (r PutScriptRequest) Do(ctx context.Context, transport Transport) (*Respons
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.rank_eval.go b/opensearchapi/api.rank_eval.go
index 68a819e..94489a2 100644
--- a/opensearchapi/api.rank_eval.go
+++ b/opensearchapi/api.rank_eval.go
@@ -172,7 +172,7 @@ func (r RankEvalRequest) Do(ctx context.Context, transport Transport) (*Response
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.reindex.go b/opensearchapi/api.reindex.go
index 2311ecb..0bccd51 100644
--- a/opensearchapi/api.reindex.go
+++ b/opensearchapi/api.reindex.go
@@ -187,7 +187,7 @@ func (r ReindexRequest) Do(ctx context.Context, transport Transport) (*Response,
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.reindex_rethrottle.go b/opensearchapi/api.reindex_rethrottle.go
index f51b414..bc4fb55 100644
--- a/opensearchapi/api.reindex_rethrottle.go
+++ b/opensearchapi/api.reindex_rethrottle.go
@@ -148,7 +148,7 @@ func (r ReindexRethrottleRequest) Do(ctx context.Context, transport Transport) (
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.render_search_template.go b/opensearchapi/api.render_search_template.go
index d5ddfdb..02a293a 100644
--- a/opensearchapi/api.render_search_template.go
+++ b/opensearchapi/api.render_search_template.go
@@ -150,7 +150,7 @@ func (r RenderSearchTemplateRequest) Do(ctx context.Context, transport Transport
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.scripts_painless_execute.go b/opensearchapi/api.scripts_painless_execute.go
index 818954d..2c26c3d 100644
--- a/opensearchapi/api.scripts_painless_execute.go
+++ b/opensearchapi/api.scripts_painless_execute.go
@@ -143,7 +143,7 @@ func (r ScriptsPainlessExecuteRequest) Do(ctx context.Context, transport Transpo
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.scroll.go b/opensearchapi/api.scroll.go
index 3298281..f6b5e28 100644
--- a/opensearchapi/api.scroll.go
+++ b/opensearchapi/api.scroll.go
@@ -160,7 +160,7 @@ func (r ScrollRequest) Do(ctx context.Context, transport Transport) (*Response,
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.search.go b/opensearchapi/api.search.go
index 8179578..9797e54 100644
--- a/opensearchapi/api.search.go
+++ b/opensearchapi/api.search.go
@@ -367,7 +367,7 @@ func (r SearchRequest) Do(ctx context.Context, transport Transport) (*Response,
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.search_shards.go b/opensearchapi/api.search_shards.go
index 43d1e21..2e8bc30 100644
--- a/opensearchapi/api.search_shards.go
+++ b/opensearchapi/api.search_shards.go
@@ -173,7 +173,7 @@ func (r SearchShardsRequest) Do(ctx context.Context, transport Transport) (*Resp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.search_template.go b/opensearchapi/api.search_template.go
index 2edb1d1..376950b 100644
--- a/opensearchapi/api.search_template.go
+++ b/opensearchapi/api.search_template.go
@@ -218,7 +218,7 @@ func (r SearchTemplateRequest) Do(ctx context.Context, transport Transport) (*Re
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.snapshot.cleanup_repository.go b/opensearchapi/api.snapshot.cleanup_repository.go
index c04785e..319c152 100644
--- a/opensearchapi/api.snapshot.cleanup_repository.go
+++ b/opensearchapi/api.snapshot.cleanup_repository.go
@@ -158,7 +158,7 @@ func (r SnapshotCleanupRepositoryRequest) Do(ctx context.Context, transport Tran
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.snapshot.clone.go b/opensearchapi/api.snapshot.clone.go
index 2642eab..a8ade0c 100644
--- a/opensearchapi/api.snapshot.clone.go
+++ b/opensearchapi/api.snapshot.clone.go
@@ -166,7 +166,7 @@ func (r SnapshotCloneRequest) Do(ctx context.Context, transport Transport) (*Res
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.snapshot.create.go b/opensearchapi/api.snapshot.create.go
index a6adc9d..b29a9dd 100644
--- a/opensearchapi/api.snapshot.create.go
+++ b/opensearchapi/api.snapshot.create.go
@@ -167,7 +167,7 @@ func (r SnapshotCreateRequest) Do(ctx context.Context, transport Transport) (*Re
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.snapshot.create_repository.go b/opensearchapi/api.snapshot.create_repository.go
index cfbf684..ab2a116 100644
--- a/opensearchapi/api.snapshot.create_repository.go
+++ b/opensearchapi/api.snapshot.create_repository.go
@@ -169,7 +169,7 @@ func (r SnapshotCreateRepositoryRequest) Do(ctx context.Context, transport Trans
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.snapshot.delete.go b/opensearchapi/api.snapshot.delete.go
index 376fcdb..dcceef7 100644
--- a/opensearchapi/api.snapshot.delete.go
+++ b/opensearchapi/api.snapshot.delete.go
@@ -154,7 +154,7 @@ func (r SnapshotDeleteRequest) Do(ctx context.Context, transport Transport) (*Re
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.snapshot.delete_repository.go b/opensearchapi/api.snapshot.delete_repository.go
index a9d919f..7aaa3c7 100644
--- a/opensearchapi/api.snapshot.delete_repository.go
+++ b/opensearchapi/api.snapshot.delete_repository.go
@@ -156,7 +156,7 @@ func (r SnapshotDeleteRepositoryRequest) Do(ctx context.Context, transport Trans
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.snapshot.get.go b/opensearchapi/api.snapshot.get.go
index 5a5b0e1..8cdb685 100644
--- a/opensearchapi/api.snapshot.get.go
+++ b/opensearchapi/api.snapshot.get.go
@@ -175,7 +175,7 @@ func (r SnapshotGetRequest) Do(ctx context.Context, transport Transport) (*Respo
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.snapshot.get_repository.go b/opensearchapi/api.snapshot.get_repository.go
index 3ad81f4..0466a90 100644
--- a/opensearchapi/api.snapshot.get_repository.go
+++ b/opensearchapi/api.snapshot.get_repository.go
@@ -159,7 +159,7 @@ func (r SnapshotGetRepositoryRequest) Do(ctx context.Context, transport Transpor
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.snapshot.restore.go b/opensearchapi/api.snapshot.restore.go
index 56823d5..46eb158 100644
--- a/opensearchapi/api.snapshot.restore.go
+++ b/opensearchapi/api.snapshot.restore.go
@@ -169,7 +169,7 @@ func (r SnapshotRestoreRequest) Do(ctx context.Context, transport Transport) (*R
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.snapshot.status.go b/opensearchapi/api.snapshot.status.go
index b44e97b..ee3be0e 100644
--- a/opensearchapi/api.snapshot.status.go
+++ b/opensearchapi/api.snapshot.status.go
@@ -166,7 +166,7 @@ func (r SnapshotStatusRequest) Do(ctx context.Context, transport Transport) (*Re
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.snapshot.verify_repository.go b/opensearchapi/api.snapshot.verify_repository.go
index cfd7152..0d37d25 100644
--- a/opensearchapi/api.snapshot.verify_repository.go
+++ b/opensearchapi/api.snapshot.verify_repository.go
@@ -158,7 +158,7 @@ func (r SnapshotVerifyRepositoryRequest) Do(ctx context.Context, transport Trans
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.tasks.cancel.go b/opensearchapi/api.tasks.cancel.go
index 5996a4b..7007249 100644
--- a/opensearchapi/api.tasks.cancel.go
+++ b/opensearchapi/api.tasks.cancel.go
@@ -167,7 +167,7 @@ func (r TasksCancelRequest) Do(ctx context.Context, transport Transport) (*Respo
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.tasks.get.go b/opensearchapi/api.tasks.get.go
index 42d00b4..e415b22 100644
--- a/opensearchapi/api.tasks.get.go
+++ b/opensearchapi/api.tasks.get.go
@@ -154,7 +154,7 @@ func (r TasksGetRequest) Do(ctx context.Context, transport Transport) (*Response
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.tasks.list.go b/opensearchapi/api.tasks.list.go
index a0ffaad..061aea7 100644
--- a/opensearchapi/api.tasks.list.go
+++ b/opensearchapi/api.tasks.list.go
@@ -174,7 +174,7 @@ func (r TasksListRequest) Do(ctx context.Context, transport Transport) (*Respons
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.terms_enum.go b/opensearchapi/api.terms_enum.go
index f671d3d..83735ac 100644
--- a/opensearchapi/api.terms_enum.go
+++ b/opensearchapi/api.terms_enum.go
@@ -148,7 +148,7 @@ func (r TermsEnumRequest) Do(ctx context.Context, transport Transport) (*Respons
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.termvectors.go b/opensearchapi/api.termvectors.go
index 7b1fefd..d670880 100644
--- a/opensearchapi/api.termvectors.go
+++ b/opensearchapi/api.termvectors.go
@@ -208,7 +208,7 @@ func (r TermvectorsRequest) Do(ctx context.Context, transport Transport) (*Respo
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.update.go b/opensearchapi/api.update.go
index 59263fb..6144260 100644
--- a/opensearchapi/api.update.go
+++ b/opensearchapi/api.update.go
@@ -212,7 +212,7 @@ func (r UpdateRequest) Do(ctx context.Context, transport Transport) (*Response,
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.update_by_query.go b/opensearchapi/api.update_by_query.go
index 4f5d5c5..22d30be 100644
--- a/opensearchapi/api.update_by_query.go
+++ b/opensearchapi/api.update_by_query.go
@@ -326,7 +326,7 @@ func (r UpdateByQueryRequest) Do(ctx context.Context, transport Transport) (*Res
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/api.update_by_query_rethrottle.go b/opensearchapi/api.update_by_query_rethrottle.go
index 2bf5d50..9cc0939 100644
--- a/opensearchapi/api.update_by_query_rethrottle.go
+++ b/opensearchapi/api.update_by_query_rethrottle.go
@@ -148,7 +148,7 @@ func (r UpdateByQueryRethrottleRequest) Do(ctx context.Context, transport Transp
 		Header:     res.Header,
 	}
 
-	return &response, nil
+	return &response, response.Err()
 }
 
 // WithContext sets the request context.
diff --git a/opensearchapi/opensearchapi.error.go b/opensearchapi/opensearchapi.error.go
new file mode 100644
index 0000000..cfaa155
--- /dev/null
+++ b/opensearchapi/opensearchapi.error.go
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+// Modifications Copyright OpenSearch Contributors. See
+// GitHub history for details.
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package opensearchapi
+
+import "fmt"
+
+// Error represents the API error response.
+type Error struct {
+	Err    Err `json:"error"`
+	Status int `json:"status"`
+}
+
+// Err represents the error of an API error response
+type Err struct {
+	RootCause []RootCause `json:"root_cause"`
+	Type      string      `json:"type"`
+	Reason    string      `json:"reason"`
+	Index     string      `json:"index,omitempty"`
+	IndexUUID string      `json:"index_uudi,omitempty"`
+}
+
+// RootCause represents the root_cause of an API error response
+type RootCause struct {
+	Type      string `json:"type"`
+	Reason    string `json:"reason"`
+	Index     string `json:"index,omitempty"`
+	IndexUUID string `json:"index_uudi,omitempty"`
+}
+
+// Error returns a string.
+func (e *Error) Error() string {
+	return fmt.Sprintf("error: %s, status: %d", e.Err, e.Status)
+}
diff --git a/opensearchapi/opensearchapi.response.go b/opensearchapi/opensearchapi.response.go
index a0cb074..d4344ee 100644
--- a/opensearchapi/opensearchapi.response.go
+++ b/opensearchapi/opensearchapi.response.go
@@ -28,6 +28,7 @@ package opensearchapi
 
 import (
 	"bytes"
+	"encoding/json"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -37,7 +38,6 @@ import (
 )
 
 // Response represents the API response.
-//
 type Response struct {
 	StatusCode int
 	Header     http.Header
@@ -47,7 +47,6 @@ type Response struct {
 // String returns the response as a string.
 //
 // The intended usage is for testing or debugging only.
-//
 func (r *Response) String() string {
 	var (
 		out = new(bytes.Buffer)
@@ -84,7 +83,6 @@ func (r *Response) String() string {
 }
 
 // Status returns the response status as a string.
-//
 func (r *Response) Status() string {
 	var b strings.Builder
 	if r != nil {
@@ -96,19 +94,36 @@ func (r *Response) Status() string {
 }
 
 // IsError returns true when the response status indicates failure.
-//
 func (r *Response) IsError() bool {
 	return r.StatusCode > 299
 }
 
 // Warnings returns the deprecation warnings from response headers.
-//
 func (r *Response) Warnings() []string {
 	return r.Header["Warning"]
 }
 
 // HasWarnings returns true when the response headers contain deprecation warnings.
-//
 func (r *Response) HasWarnings() bool {
 	return len(r.Warnings()) > 0
 }
+
+// Err returns an error when the response status indicates failures.
+func (r *Response) Err() error {
+	if r.IsError() {
+		if r.Body == nil {
+			return fmt.Errorf(r.Status())
+		}
+		body, err := io.ReadAll(r.Body)
+		if err != nil {
+			return fmt.Errorf(r.Status())
+		}
+		var e *Error
+		err = json.Unmarshal(body, &e)
+		if err == nil {
+			return e
+		}
+		return fmt.Errorf("status: %d, error: %s", r.StatusCode, string(body))
+	}
+	return nil
+}


Test patch:
diff --git a/opensearchapi/api.indices.datastream_test.go b/opensearchapi/api.indices.datastream_test.go
new file mode 100644
index 0000000..2fe9fc8
--- /dev/null
+++ b/opensearchapi/api.indices.datastream_test.go
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+// Modifications Copyright OpenSearch Contributors. See
+// GitHub history for details.
+
+//go:build integration
+// +build integration
+
+package opensearchapi_test
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"fmt"
+	"github.com/opensearch-project/opensearch-go/v2"
+	"github.com/opensearch-project/opensearch-go/v2/opensearchapi"
+	"github.com/stretchr/testify/require"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"testing"
+	"time"
+)
+
+type DataStreamRequest interface {
+	Do(context.Context, opensearchapi.Transport) (*opensearchapi.Response, error)
+}
+
+func TestIndicesDataStreams_Do(t *testing.T) {
+	name := fmt.Sprintf("demo-%s", time.Now().Format("2006-01-02-15-04-05"))
+
+	tests := []struct {
+		name     string
+		r        DataStreamRequest
+		want     *opensearchapi.Response
+		wantBody string
+		wantErr  bool
+	}{
+		{
+			name: "TestIndicesCreateDataStreamRequest_Do",
+			r: opensearchapi.IndicesCreateDataStreamRequest{
+				Name:       name,
+				Pretty:     true,
+				Human:      true,
+				ErrorTrace: true,
+				Header: map[string][]string{
+					"Content-Type": {"application/json"},
+				},
+			},
+			want: &opensearchapi.Response{
+				StatusCode: 200,
+				Header: http.Header{
+					"Content-Type": []string{"application/json; charset=UTF-8"},
+				},
+			},
+			wantBody: `{"acknowledged":true}`,
+			wantErr:  false,
+		},
+		{
+			name: "TestIndicesGetDataStreamRequest_Do",
+			r: opensearchapi.IndicesGetDataStreamRequest{
+				Name:       name,
+				Pretty:     true,
+				Human:      true,
+				ErrorTrace: true,
+				Header: map[string][]string{
+					"Content-Type": {"application/json"},
+				},
+			},
+			want: &opensearchapi.Response{
+				StatusCode: 200,
+				Header: http.Header{
+					"Content-Type": []string{"application/json; charset=UTF-8"},
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name: "TestIndicesGetAllDataStreamsRequest_Do",
+			r: opensearchapi.IndicesGetDataStreamRequest{
+				Pretty:     true,
+				Human:      true,
+				ErrorTrace: true,
+				Header: map[string][]string{
+					"Content-Type": {"application/json"},
+				},
+			},
+			want: &opensearchapi.Response{
+				StatusCode: 200,
+				Header: http.Header{
+					"Content-Type": []string{"application/json; charset=UTF-8"},
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name: "TestIndicesGetStatsDataStreamRequest_Do",
+			r: opensearchapi.IndicesGetDataStreamStatsRequest{
+				Name:       name,
+				Pretty:     true,
+				Human:      true,
+				ErrorTrace: true,
+				Header: map[string][]string{
+					"Content-Type": {"application/json"},
+				},
+			},
+			want: &opensearchapi.Response{
+				StatusCode: 200,
+				Header: http.Header{
+					"Content-Type": []string{"application/json; charset=UTF-8"},
+				},
+			},
+			wantBody: fmt.Sprintf(`{"_shards":{"total":2,"successful":1,"failed":0},"data_stream_count":1,"backing_indices":1,"total_store_size":"208b","total_store_size_bytes":208,"data_streams":[{"data_stream":"%s","backing_indices":1,"store_size":"208b","store_size_bytes":208,"maximum_timestamp":0}]}`, name),
+			wantErr:  false,
+		},
+		{
+			name: "TestIndicesDeleteDataStreamRequest_Do",
+			r: opensearchapi.IndicesDeleteDataStreamRequest{
+				Name:       name,
+				Pretty:     true,
+				Human:      true,
+				ErrorTrace: true,
+				Header: map[string][]string{
+					"Content-Type": {"application/json"},
+				},
+			},
+			want: &opensearchapi.Response{
+				StatusCode: 200,
+				Header: http.Header{
+					"Content-Type": []string{"application/json; charset=UTF-8"},
+				},
+			},
+			wantBody: `{"acknowledged":true}`,
+			wantErr:  false,
+		},
+	}
+
+	client, err := opensearch.NewDefaultClient()
+	require.NoError(t, err)
+
+	iPut := opensearchapi.IndicesPutIndexTemplateRequest{
+		Name:       fmt.Sprintf("demo-data-template"),
+		Pretty:     true,
+		Human:      true,
+		ErrorTrace: true,
+		Body:       strings.NewReader(fmt.Sprintf(`{"index_patterns": ["demo-*"], "data_stream": {}, "priority": 100} }`)),
+	}
+
+	iPutResponse, err := iPut.Do(context.Background(), client)
+	require.NoError(t, err)
+	require.Equalf(t, false, iPutResponse.IsError(),
+		"Error when creating index template: %s", iPutResponse.String())
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := tt.r.Do(context.Background(), client)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Do() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+
+			require.Equalf(t, got.IsError(), tt.wantErr, "Do() got = %v, want %v", got.IsError(), tt.wantErr)
+			require.Equalf(t, got.StatusCode, tt.want.StatusCode, "Do() got = %v, want %v", got.StatusCode, tt.want.StatusCode)
+
+			if tt.wantBody != "" {
+				require.Equalf(t, got.Header, tt.want.Header, "Do() got = %v, want %v", got.Header, tt.want.Header)
+
+				defer got.Body.Close()
+				body, err := ioutil.ReadAll(got.Body)
+				require.NoError(t, err)
+
+				buffer := new(bytes.Buffer)
+				if err := json.Compact(buffer, body); err != nil {
+					fmt.Println(err)
+				}
+
+				require.Equalf(t, buffer.String(), tt.wantBody, "Do() got = %v, want %v", got.String(), tt.wantBody)
+			}
+		})
+	}
+}
diff --git a/opensearchapi/opensearchapi_benchmark_test.go b/opensearchapi/opensearchapi_benchmark_test.go
index 2462760..5b90955 100644
--- a/opensearchapi/opensearchapi_benchmark_test.go
+++ b/opensearchapi/opensearchapi_benchmark_test.go
@@ -28,6 +28,7 @@ package opensearchapi_test
 
 import (
 	"context"
+	"errors"
 	"io/ioutil"
 	"net/http"
 	"strconv"
@@ -277,7 +278,10 @@ func BenchmarkAPI(b *testing.B) {
 			_, err := fakeClientWithError.Search(fakeClientWithError.Search.WithContext(context.Background()), fakeClientWithError.Search.WithBody(body))
 
 			if err != nil {
-				b.Errorf("Unexpected error when getting a response: %s", err)
+				var opensearchError *opensearchapi.Error
+				if !errors.As(err, &opensearchError) {
+					b.Errorf("Unexpected error when getting a response: %s", err)
+				}
 			}
 		}
 	})
@@ -301,7 +305,10 @@ func BenchmarkAPI(b *testing.B) {
 		for i := 0; i < b.N; i++ {
 			req := opensearchapi.SearchRequest{Body: body}
 			if _, err := req.Do(context.Background(), fakeClientWithError); err != nil {
-				b.Errorf("Unexpected error when getting a response: %s", err)
+				var opensearchError *opensearchapi.Error
+				if !errors.As(err, &opensearchError) {
+					b.Errorf("Unexpected error when getting a response: %s", err)
+				}
 			}
 		}
 	})
diff --git a/opensearchapi/opensearchapi_integration_test.go b/opensearchapi/opensearchapi_integration_test.go
index 218ce86..8690b11 100644
--- a/opensearchapi/opensearchapi_integration_test.go
+++ b/opensearchapi/opensearchapi_integration_test.go
@@ -24,6 +24,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
+//go:build integration
 // +build integration
 
 package opensearchapi_test
@@ -54,10 +55,6 @@ func TestAPI(t *testing.T) {
 		}
 		defer res.Body.Close()
 
-		if res.IsError() {
-			t.Fatalf("Error response: %s", res.String())
-		}
-
 		var d map[string]interface{}
 		err = json.NewDecoder(res.Body).Decode(&d)
 		if err != nil {
@@ -78,10 +75,6 @@ func TestAPI(t *testing.T) {
 		}
 		defer res.Body.Close()
 
-		if res.IsError() {
-			t.Fatalf("Error response: %s", res.String())
-		}
-
 		if !strings.HasPrefix(res.String(), "[200 OK] ---") {
 			t.Errorf("Unexpected response body: doesn't start with '[200 OK] ---'; %s", res.String())
 		}
@@ -121,10 +114,7 @@ func TestAPI(t *testing.T) {
 		if err != nil {
 			t.Fatalf("Failed to index data: %s", err)
 		}
-		res.Body.Close()
-		if res.IsError() {
-			t.Fatalf("Failed to index data: %s", res.Status())
-		}
+		defer res.Body.Close()
 
 		// Launch reindexing task with wait_for_completion=false
 		//
@@ -136,9 +126,6 @@ func TestAPI(t *testing.T) {
 		if err != nil {
 			t.Fatalf("Failed to reindex: %s", err)
 		}
-		if res.IsError() {
-			t.Fatalf("Failed to reindex: %s", res.Status())
-		}
 		time.Sleep(10 * time.Millisecond)
 
 		res, err = client.Tasks.List(client.Tasks.List.WithPretty())
@@ -146,9 +133,6 @@ func TestAPI(t *testing.T) {
 			t.Fatalf("ERROR: %s", err)
 		}
 		res.Body.Close()
-		if res.IsError() {
-			t.Fatalf("Failed to get tasks: %s", res.Status())
-		}
 
 		// Get the list of tasks
 		//
@@ -158,10 +142,6 @@ func TestAPI(t *testing.T) {
 		}
 		defer res.Body.Close()
 
-		if res.IsError() {
-			t.Fatalf("Failed to get tasks: %s", res.Status())
-		}
-
 		type task struct {
 			Node        string
 			ID          int
@@ -206,10 +186,7 @@ func TestAPI(t *testing.T) {
 						if err != nil {
 							t.Fatalf("ERROR: %s", err)
 						}
-						res.Body.Close()
-						if res.IsError() {
-							t.Fatalf("Failed to cancel task: %s", res)
-						}
+						defer res.Body.Close()
 					}
 				}
 			}
diff --git a/opensearchapi/opensearchapi_response_internal_test.go b/opensearchapi/opensearchapi_response_internal_test.go
index 80c786f..57fd6a9 100644
--- a/opensearchapi/opensearchapi_response_internal_test.go
+++ b/opensearchapi/opensearchapi_response_internal_test.go
@@ -24,12 +24,14 @@
 // specific language governing permissions and limitations
 // under the License.
 
+//go:build !integration
 // +build !integration
 
 package opensearchapi
 
 import (
 	"errors"
+	"io"
 	"io/ioutil"
 	"net/http"
 	"strings"
@@ -103,6 +105,59 @@ func TestAPIResponse(t *testing.T) {
 		}
 	})
 
+	t.Run("Error", func(t *testing.T) {
+		res = &Response{StatusCode: 201}
+
+		if err := res.Err(); err != nil {
+			t.Errorf("Unexpected error for response: %s", res.Status())
+		}
+
+		res = &Response{StatusCode: 403}
+
+		if err := res.Err(); err == nil {
+			t.Errorf("Expected error for response: %s", res.Status())
+		}
+
+		res = &Response{
+			StatusCode: 400,
+			Body: io.NopCloser(
+				strings.NewReader(`
+				{
+					"error":{
+						"root_cause":[{
+							"type":"resource_already_exists_exception",
+							"reason":"index [test/HU2mN_RMRXGcS38j3yV-VQ] already exists",
+							"index":"test",
+							"index_uuid":"HU2mN_RMRXGcS38j3yV-VQ"
+						}],
+						"type":"resource_already_exists_exception",
+						"reason":"index [test/HU2mN_RMRXGcS38j3yV-VQ] already exists",
+						"index":"test",
+						"index_uuid":"HU2mN_RMRXGcS38j3yV-VQ"
+					},
+					"status":400
+				}`)),
+		}
+
+		err := res.Err()
+		if err == nil {
+			t.Errorf("Expected error for response: %s", res.Status())
+		}
+		var errTest *Error
+		if !errors.As(err, &errTest) {
+			t.Errorf("Expected error to be of type opensearchapi.Error: %T", err)
+		}
+		if errTest.Status != 400 ||
+			errTest.Err.Reason != "index [test/HU2mN_RMRXGcS38j3yV-VQ] already exists" ||
+			errTest.Err.Type != "resource_already_exists_exception" ||
+			len(errTest.Err.RootCause) != 1 ||
+			errTest.Err.RootCause[0].Type != "resource_already_exists_exception" ||
+			errTest.Err.RootCause[0].Reason != "index [test/HU2mN_RMRXGcS38j3yV-VQ] already exists" {
+
+			t.Errorf("Reponse Error was not parsed correctly")
+		}
+	})
+
 	t.Run("Warnings", func(t *testing.T) {
 		hdr := http.Header{}
 		hdr.Add("Warning", "Foo 1")
diff --git a/opensearchutil/json_reader_integration_test.go b/opensearchutil/json_reader_integration_test.go
index 389626a..3e87133 100644
--- a/opensearchutil/json_reader_integration_test.go
+++ b/opensearchutil/json_reader_integration_test.go
@@ -24,6 +24,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
+//go:build integration
 // +build integration
 
 package opensearchutil_test
@@ -61,10 +62,6 @@ func TestJSONReaderIntegration(t *testing.T) {
 		}
 		defer res.Body.Close()
 
-		if res.IsError() {
-			t.Fatalf("Error response: %s", res.String())
-		}
-
 		query := map[string]interface{}{
 			"query": map[string]interface{}{
 				"match": map[string]interface{}{
@@ -83,10 +80,6 @@ func TestJSONReaderIntegration(t *testing.T) {
 		}
 		defer res.Body.Close()
 
-		if res.IsError() {
-			t.Errorf("Error response: %s", res)
-		}
-
 		if !strings.Contains(res.String(), "Foo Bar") {
 			t.Errorf("Unexpected response: %s", res)
 		}