diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index dd7ced1..26b1ce2 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.49.0"
+ ".": "0.50.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index ad1b0f2..f0ba92f 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 111
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-49a1a92e00d1eb87e91e8527275cb0705fce2edea30e70fea745f134dd451fbd.yml
-openapi_spec_hash: 0ffef6a95f9d9b1096180fc5e4c5b39c
-config_hash: 9818dd634f87b677410eefd013d7a179
+configured_endpoints: 112
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-930823e8b25b4644b74098ad5479840f64a329321aa236460f8a9562ae9051bf.yml
+openapi_spec_hash: 9f868e67df8fd2fec8d8fc3eb5ba0b26
+config_hash: 08d55086449943a8fec212b870061a3f
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee9c27d..a2258a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 0.50.0 (2026-04-13)
+
+Full Changelog: [v0.49.0...v0.50.0](https://github.com/kernel/kernel-go-sdk/compare/v0.49.0...v0.50.0)
+
+### Features
+
+* add POST /browsers/{id}/curl and /curl/raw endpoints ([94b52ae](https://github.com/kernel/kernel-go-sdk/commit/94b52aeac38b20c8ea60e3c016443e0ccc680451))
+
## 0.49.0 (2026-04-10)
Full Changelog: [v0.48.0...v0.49.0](https://github.com/kernel/kernel-go-sdk/compare/v0.48.0...v0.49.0)
diff --git a/README.md b/README.md
index f88b778..25c70d8 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ Or to pin the version:
```sh
-go get -u 'github.com/kernel/kernel-go-sdk@v0.49.0'
+go get -u 'github.com/kernel/kernel-go-sdk@v0.50.0'
```
diff --git a/api.md b/api.md
index 89832f0..4f475a5 100644
--- a/api.md
+++ b/api.md
@@ -82,6 +82,7 @@ Response Types:
- kernel.BrowserGetResponse
- kernel.BrowserUpdateResponse
- kernel.BrowserListResponse
+- kernel.BrowserCurlResponse
Methods:
@@ -90,6 +91,7 @@ Methods:
- client.Browsers.Update(ctx context.Context, id string, body kernel.BrowserUpdateParams) (\*kernel.BrowserUpdateResponse, error)
- client.Browsers.List(ctx context.Context, query kernel.BrowserListParams) (\*pagination.OffsetPagination[kernel.BrowserListResponse], error)
- client.Browsers.Delete(ctx context.Context, body kernel.BrowserDeleteParams) error
+- client.Browsers.Curl(ctx context.Context, id string, body kernel.BrowserCurlParams) (\*kernel.BrowserCurlResponse, error)
- client.Browsers.DeleteByID(ctx context.Context, id string) error
- client.Browsers.LoadExtensions(ctx context.Context, id string, body kernel.BrowserLoadExtensionsParams) error
diff --git a/browser.go b/browser.go
index 6478cdf..4533fc1 100644
--- a/browser.go
+++ b/browser.go
@@ -133,6 +133,20 @@ func (r *BrowserService) Delete(ctx context.Context, body BrowserDeleteParams, o
return err
}
+// Sends an HTTP request through Chrome's HTTP request stack, inheriting the
+// browser's TLS fingerprint, cookies, proxy configuration, and headers. Returns a
+// structured JSON response with status, headers, body, and timing.
+func (r *BrowserService) Curl(ctx context.Context, id string, body BrowserCurlParams, opts ...option.RequestOption) (res *BrowserCurlResponse, err error) {
+ opts = slices.Concat(r.Options, opts)
+ if id == "" {
+ err = errors.New("missing required id parameter")
+ return nil, err
+ }
+ path := fmt.Sprintf("browsers/%s/curl", id)
+ err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
+ return res, err
+}
+
// Delete a browser session by ID
func (r *BrowserService) DeleteByID(ctx context.Context, id string, opts ...option.RequestOption) (err error) {
opts = slices.Concat(r.Options, opts)
@@ -609,6 +623,33 @@ func (r *BrowserListResponse) UnmarshalJSON(data []byte) error {
return apijson.UnmarshalRoot(data, r)
}
+// Structured response from the browser curl request.
+type BrowserCurlResponse struct {
+ // Response body (UTF-8 string or base64 depending on request).
+ Body string `json:"body" api:"required"`
+ // Total request duration in milliseconds.
+ DurationMs int64 `json:"duration_ms" api:"required"`
+ // Response headers (multi-value).
+ Headers map[string][]string `json:"headers" api:"required"`
+ // HTTP status code from target.
+ Status int64 `json:"status" api:"required"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Body respjson.Field
+ DurationMs respjson.Field
+ Headers respjson.Field
+ Status respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r BrowserCurlResponse) RawJSON() string { return r.JSON.raw }
+func (r *BrowserCurlResponse) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type BrowserNewParams struct {
// If true, enables GPU acceleration for the browser session. Requires Start-Up or
// Enterprise plan and headless=false.
@@ -770,6 +811,55 @@ func (r BrowserDeleteParams) URLQuery() (v url.Values, err error) {
})
}
+type BrowserCurlParams struct {
+ // Target URL (must be http or https).
+ URL string `json:"url" api:"required"`
+ // Request body (for POST/PUT/PATCH).
+ Body param.Opt[string] `json:"body,omitzero"`
+ // Request timeout in milliseconds.
+ TimeoutMs param.Opt[int64] `json:"timeout_ms,omitzero"`
+ // Custom headers merged with browser defaults.
+ Headers map[string]string `json:"headers,omitzero"`
+ // HTTP method.
+ //
+ // Any of "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS".
+ Method BrowserCurlParamsMethod `json:"method,omitzero"`
+ // Encoding for the response body. Use base64 for binary content.
+ //
+ // Any of "utf8", "base64".
+ ResponseEncoding BrowserCurlParamsResponseEncoding `json:"response_encoding,omitzero"`
+ paramObj
+}
+
+func (r BrowserCurlParams) MarshalJSON() (data []byte, err error) {
+ type shadow BrowserCurlParams
+ return param.MarshalObject(r, (*shadow)(&r))
+}
+func (r *BrowserCurlParams) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// HTTP method.
+type BrowserCurlParamsMethod string
+
+const (
+ BrowserCurlParamsMethodGet BrowserCurlParamsMethod = "GET"
+ BrowserCurlParamsMethodHead BrowserCurlParamsMethod = "HEAD"
+ BrowserCurlParamsMethodPost BrowserCurlParamsMethod = "POST"
+ BrowserCurlParamsMethodPut BrowserCurlParamsMethod = "PUT"
+ BrowserCurlParamsMethodPatch BrowserCurlParamsMethod = "PATCH"
+ BrowserCurlParamsMethodDelete BrowserCurlParamsMethod = "DELETE"
+ BrowserCurlParamsMethodOptions BrowserCurlParamsMethod = "OPTIONS"
+)
+
+// Encoding for the response body. Use base64 for binary content.
+type BrowserCurlParamsResponseEncoding string
+
+const (
+ BrowserCurlParamsResponseEncodingUtf8 BrowserCurlParamsResponseEncoding = "utf8"
+ BrowserCurlParamsResponseEncodingBase64 BrowserCurlParamsResponseEncoding = "base64"
+)
+
type BrowserLoadExtensionsParams struct {
// List of extensions to upload and activate
Extensions []BrowserLoadExtensionsParamsExtension `json:"extensions,omitzero" api:"required"`
diff --git a/browser_test.go b/browser_test.go
index 40b7829..c8681a6 100644
--- a/browser_test.go
+++ b/browser_test.go
@@ -190,6 +190,42 @@ func TestBrowserDelete(t *testing.T) {
}
}
+func TestBrowserCurlWithOptionalParams(t *testing.T) {
+ t.Skip("Mock server tests are disabled")
+ baseURL := "http://localhost:4010"
+ if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
+ baseURL = envURL
+ }
+ if !testutil.CheckTestServer(t, baseURL) {
+ return
+ }
+ client := kernel.NewClient(
+ option.WithBaseURL(baseURL),
+ option.WithAPIKey("My API Key"),
+ )
+ _, err := client.Browsers.Curl(
+ context.TODO(),
+ "id",
+ kernel.BrowserCurlParams{
+ URL: "url",
+ Body: kernel.String("body"),
+ Headers: map[string]string{
+ "foo": "string",
+ },
+ Method: kernel.BrowserCurlParamsMethodGet,
+ ResponseEncoding: kernel.BrowserCurlParamsResponseEncodingUtf8,
+ TimeoutMs: kernel.Int(1000),
+ },
+ )
+ if err != nil {
+ var apierr *kernel.Error
+ if errors.As(err, &apierr) {
+ t.Log(string(apierr.DumpRequest(true)))
+ }
+ t.Fatalf("err should be nil: %s", err.Error())
+ }
+}
+
func TestBrowserDeleteByID(t *testing.T) {
t.Skip("Mock server tests are disabled")
baseURL := "http://localhost:4010"
diff --git a/internal/version.go b/internal/version.go
index 845a2d5..9fc0bbd 100644
--- a/internal/version.go
+++ b/internal/version.go
@@ -2,4 +2,4 @@
package internal
-const PackageVersion = "0.49.0" // x-release-please-version
+const PackageVersion = "0.50.0" // x-release-please-version