Skip to content
Hoody.com

The Hoody Browser service exposes page-level operations: navigate to a URL, evaluate JavaScript, extract page content as HTML, text, or PDF, take screenshots, capture accessibility snapshots, and drive the page with element actions. All endpoints target /api/v1/browser/<path> and are served by the browser container at https://{projectId}-{containerId}-browser-1.{server}.containers.hoody.com. Reach the service through the Hoody proxy; the management API hosts do not serve these routes.

Opens a new tab (or reuses an existing one) and navigates to a URL. By default a new tab is created and becomes active. Pass onlyIfNotExists=true to reuse a tab already on the target URL; pass active=false to keep the current active tab focused.

NameInTypeRequiredDescription
browser_idquerystringNoSelects the instance: SDK and CLI clients send the request to the browser-<N> service host. A value that conflicts with the host’s instance is refused with 400 INSTANCE_SELECTOR_CONFLICT. Against a bare server it selects nothing; address instances there with browser_host + browser_port.
startquerybooleanNoControls instance creation. Default mode: instances are created automatically; set to false to prevent creation. When auto-start is disabled globally, set to true to create an instance. The parameter intentionally declares no schema default: omitting it is not equivalent to sending true. The server branches on HOODY_DISABLE_AUTO_START (DISABLE ? start === 'true' : start !== 'false'), so an absent value means “auto-start unless the operator disabled it” while an explicit true means “start even though the operator disabled it”.
urlquerystringNoThe URL to navigate to. Repeating this key in the query string is a 400 VALIDATION_ERROR (url must not be repeated). A JSON body property named url is governed by the body schema.
tabIdqueryintegerNoThe ID of the tab to interact with (from /tabs). Omitted: the active tab. Supplied: exactly that tab; an unknown id returns 404 TAB_NOT_FOUND (with details.openTabs) and a malformed id returns 400 VALIDATION_ERROR; the request never falls back to another tab. Repeating this key in the query string is a 400 VALIDATION_ERROR (tabId must not be repeated).
waitUntilquerystringNoNavigation readiness to wait for. One of commit, domcontentloaded, or load (default load). networkidle is deliberately not offered. Repeating this key in the query string is a 400 VALIDATION_ERROR (waitUntil must not be repeated).
timeoutMsqueryintegerNoBudget for viewport settling and navigation, in milliseconds. Omitted: legacy timing (Playwright’s 30 s default); the parent still bounds the request with --request-timeout. Present: returns 504 TIMEOUT (phase navigation) on expiry and the parent bounds the request by timeoutMs + 5 s. Repeating this key in the query string is a 400 VALIDATION_ERROR (timeoutMs must not be repeated).
instanceGenerationquerystringNoFrom /metadata; mismatch returns 409 INSTANCE_CHANGED (also when no instance exists; no instance is spawned). Repeating this key in the query string is a 400 VALIDATION_ERROR (instanceGeneration must not be repeated).
activequerybooleanNoMake the tab active (focused) after navigation. Default true. Strict boolean: only true or false. Repeating the key is not an error here; the instance reads the last occurrence.
onlyIfNotExistsquerybooleanNoReuse an existing tab whose URL already equals the target instead of opening a new one (the reused tab is still navigated; this is tab reuse, not a navigation dedup). Default false. Same strict boolean rule as active.
ignoreGetParametersquerybooleanNoIgnore query parameters when checking for existing URL. Default false. Same strict boolean rule as active.
Terminal window
curl -X GET "https://{projectId}-{containerId}-browser-1.{server}.containers.hoody.com/api/v1/browser/browse?url=https%3A%2F%2Fexample.com&waitUntil=load"
{
"instanceGeneration": "gen_4a1c9f2b7d3e5081",
"tabId": 3,
"url": "https://github.com/",
"requestedUrl": "https://github.com",
"title": "GitHub",
"httpStatus": 200,
"created": true,
"reused": false,
"elapsedMs": 812
}

Same behavior as GET /api/v1/browser/browse, but the navigation parameters are sent as a JSON body instead of a query string. Useful for scripts whose URL contains characters that are awkward to URL-encode.

NameInTypeRequiredDescription
browser_idquerystringNoSelects the instance: SDK and CLI clients send the request to the browser-<N> service host. A value that conflicts with the host’s instance is refused with 400 INSTANCE_SELECTOR_CONFLICT. Against a bare server it selects nothing; address instances there with browser_host + browser_port.
startquerybooleanNoControls instance creation, same semantics as on GET /api/v1/browser/browse.
NameTypeRequiredDescription
urlstringYesThe URL to navigate to.
tabIdintegerNoTab to navigate in (from /tabs); omitted uses the active tab, unknown returns 404 TAB_NOT_FOUND.
waitUntilstringNoNavigation readiness: commit, domcontentloaded, or load (default load).
timeoutMsintegerNoBudget for navigation, in milliseconds (1 to 30000). Omitted keeps legacy timing; present budgets the navigation and returns 504 on expiry.
instanceGenerationstringNoFrom /metadata; mismatch returns 409 INSTANCE_CHANGED.
activebooleanNoWhether the tab becomes the active one. Default true. Send a JSON boolean (the strings "true" and "false" are also tolerated); any other value returns 400 VALIDATION_ERROR.
onlyIfNotExistsbooleanNoReuse an existing tab already on this URL instead of opening a new one. Default false.
ignoreGetParametersbooleanNoCompare URLs for onlyIfNotExists with the query string stripped. Default false.
Terminal window
curl -X POST "https://{projectId}-{containerId}-browser-1.{server}.containers.hoody.com/api/v1/browser/browse" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/",
"waitUntil": "load",
"timeoutMs": 15000
}'
{
"tabId": 2,
"url": "https://example.com/welcome",
"created": true,
"reused": false,
"requestedUrl": "https://example.com/",
"title": "Example Domain",
"httpStatus": 200,
"instanceGeneration": "gen-7",
"elapsedMs": 412
}

Executes a JavaScript snippet in the context of the last active tab and returns the result. Useful for short, URL-safe scripts. For longer scripts, prefer the POST variant.

NameInTypeRequiredDescription
browser_idquerystringNoSelects the instance: SDK and CLI clients send the request to the browser-<N> service host. A value that conflicts with the host’s instance is refused with 400 INSTANCE_SELECTOR_CONFLICT. Against a bare server it selects nothing; address instances there with browser_host + browser_port.
startquerybooleanNoControls instance creation, same semantics as on /browse.
tabIdqueryintegerNoThe ID of the tab to evaluate in (from /tabs). Omitted: the active tab. Supplied: exactly that tab; an unknown id returns 404 TAB_NOT_FOUND and a malformed id returns 400 VALIDATION_ERROR. Repeating this key in the query string is a 400 VALIDATION_ERROR (tabId must not be repeated).
timeoutMsqueryintegerNoTime limit for the script, in milliseconds (1 to 30000, default 30000). A script still running when it is spent is stopped and the answer is 504 TIMEOUT (details.phase evaluate). Repeating this key in the query string is a 400 VALIDATION_ERROR (timeoutMs must not be repeated).
scriptBase64querybooleanNoSet to true when script is base64-encoded (a base64: prefix on the script is also accepted). Avoids URL-encoding issues for scripts with special characters. Default false. Repeating this key in the query string is a 400 VALIDATION_ERROR (scriptBase64 must not be repeated).
scriptquerystringYesJavaScript code to execute (can be base64-encoded). Repeating this key in the query string is a 400 VALIDATION_ERROR (script must not be repeated): two copies are two different programs and only the last would run. The rule is about the query string only; a JSON body property named script is governed by the body schema.
Terminal window
curl -X GET "https://{projectId}-{containerId}-browser-1.{server}.containers.hoody.com/api/v1/browser/eval?script=document.title"

The result of the evaluated JavaScript expression. The exact shape depends on what the script returns (a primitive is JSON-encoded; an object or array is serialized as-is).

{
"result": "Example Domain"
}

Executes a JavaScript snippet provided in the request body. Accepts either an application/json body with { script, tabId, scriptBase64 } or a raw text/plain body containing the script directly.

NameInTypeRequiredDescription
browser_idquerystringNoSelects the instance: SDK and CLI clients send the request to the browser-<N> service host. A value that conflicts with the host’s instance is refused with 400 INSTANCE_SELECTOR_CONFLICT. Against a bare server it selects nothing; address instances there with browser_host + browser_port.
startquerybooleanNoControls instance creation, same semantics as on /browse.
NameTypeRequiredDescription
scriptstringYes (for JSON body)JavaScript code to execute.
tabIdintegerNoTab to evaluate in (from /tabs); omitted uses the active tab, unknown returns 404 TAB_NOT_FOUND, malformed returns 400.
scriptBase64booleanNoSet to true when script is base64-encoded.
timeoutMsintegerNoTime limit for the script, in milliseconds (1 to 30000, default 30000). A script still running when it is spent is stopped and the answer is 504 TIMEOUT (details.phase evaluate).
Terminal window
curl -X POST "https://{projectId}-{containerId}-browser-1.{server}.containers.hoody.com/api/v1/browser/eval" \
-H "Content-Type: application/json" \
-d '{
"script": "Array.from(document.querySelectorAll(\"a\")).map(a => a.href)"
}'

The result of the evaluated JavaScript expression.

{
"result": ["https://example.com/a", "https://example.com/b"]
}

Returns the full HTML content of the active page (equivalent to document.documentElement.outerHTML). The response body is text/html.

NameInTypeRequiredDescription
browser_idquerystringNoSelects the instance: SDK and CLI clients send the request to the browser-<N> service host. A value that conflicts with the host’s instance is refused with 400 INSTANCE_SELECTOR_CONFLICT. Against a bare server it selects nothing; address instances there with browser_host + browser_port.
tabIdqueryintegerNoThe ID of the tab to read from (from /tabs). Omitted: the active tab. Supplied: exactly that tab; an unknown id returns 404 TAB_NOT_FOUND and a malformed id returns 400 VALIDATION_ERROR. Repeating this key in the query string is a 400 VALIDATION_ERROR (tabId must not be repeated).
startquerybooleanNoControls instance creation, same semantics as on /browse.
Terminal window
curl -X GET "https://{projectId}-{containerId}-browser-1.{server}.containers.hoody.com/api/v1/browser/html" \
-o page.html

The full HTML of the active page as text/html.

<!DOCTYPE html>
<html>
<head><title>Example Domain</title></head>
<body>
<h1>Example Domain</h1>
<p>Reserved for documentation examples.</p>
</body>
</html>

Returns the visible text content of the active page (equivalent to document.body.innerText). The response body is text/plain.

NameInTypeRequiredDescription
browser_idquerystringNoSelects the instance: SDK and CLI clients send the request to the browser-<N> service host. A value that conflicts with the host’s instance is refused with 400 INSTANCE_SELECTOR_CONFLICT. Against a bare server it selects nothing; address instances there with browser_host + browser_port.
tabIdqueryintegerNoThe ID of the tab to read from (from /tabs). Omitted: the active tab. Supplied: exactly that tab; an unknown id returns 404 TAB_NOT_FOUND and a malformed id returns 400 VALIDATION_ERROR. Repeating this key in the query string is a 400 VALIDATION_ERROR (tabId must not be repeated).
startquerybooleanNoControls instance creation, same semantics as on /browse.
Terminal window
curl -X GET "https://{projectId}-{containerId}-browser-1.{server}.containers.hoody.com/api/v1/browser/text"

The visible text of the active page as text/plain.

Example Domain
Reserved for documentation examples.

Generates a PDF of the current page. The response body is application/pdf. Optionally navigates to a URL first.

NameInTypeRequiredDescription
browser_idquerystringNoSelects the instance: SDK and CLI clients send the request to the browser-<N> service host. A value that conflicts with the host’s instance is refused with 400 INSTANCE_SELECTOR_CONFLICT. Against a bare server it selects nothing; address instances there with browser_host + browser_port.
tabIdqueryintegerNoThe ID of the tab to read from (from /tabs). Omitted: the active tab. Supplied: exactly that tab; an unknown id returns 404 TAB_NOT_FOUND and a malformed id returns 400 VALIDATION_ERROR. Repeating this key in the query string is a 400 VALIDATION_ERROR (tabId must not be repeated).
startquerybooleanNoControls instance creation, same semantics as on /browse.
urlquerystringNoOptional URL to navigate to before generating the PDF. Repeating this key in the query string is a 400 VALIDATION_ERROR (url must not be repeated): the parent’s rule is on the key, not on the operation, so it applies here too even though this parameter is declared inline rather than shared.
formatquerystringNoPaper format such as A4 or Letter (default Letter).
landscapequerybooleanNoUse landscape orientation. Default false.
printBackgroundquerybooleanNoInclude background graphics. Default false.
marginquerystringNoUniform margin (e.g. 1cm, 0.5in).
Terminal window
curl -X GET "https://{projectId}-{containerId}-browser-1.{server}.containers.hoody.com/api/v1/browser/pdf?format=A4&printBackground=true" \
-o page.pdf

The rendered page as a PDF document. The response body is raw application/pdf bytes (binary content); there is no JSON envelope.

Captures a screenshot of a browser tab. Optionally navigates to a URL first and waits for the page to load.

NameInTypeRequiredDescription
browser_idquerystringNoSelects the instance: SDK and CLI clients send the request to the browser-<N> service host. A value that conflicts with the host’s instance is refused with 400 INSTANCE_SELECTOR_CONFLICT. Against a bare server it selects nothing; address instances there with browser_host + browser_port.
startquerybooleanNoControls instance creation, same semantics as on /browse.
urlquerystringNoThe URL to navigate to before capturing. Repeating this key in the query string is a 400 VALIDATION_ERROR (url must not be repeated).
tabIdqueryintegerNoThe ID of the tab to capture (from /tabs). Omitted: the active tab. Supplied: exactly that tab; an unknown id returns 404 TAB_NOT_FOUND and a malformed id returns 400 VALIDATION_ERROR. Repeating this key in the query string is a 400 VALIDATION_ERROR (tabId must not be repeated).
formatquerystringNoOutput format. One of png, jpeg, or base64 (default png).
qualityqueryintegerNoImage quality for the JPEG format (0 to 100).
fullPagequerybooleanNoCapture the entire scrollable page (default false).
Terminal window
curl -X GET "https://{projectId}-{containerId}-browser-1.{server}.containers.hoody.com/api/v1/browser/screenshot?url=https%3A%2F%2Fexample.com&format=png&fullPage=true" \
-o page.png

The screenshot bytes. png and jpeg return image/png or image/jpeg directly; base64 returns a JSON envelope.

{
"data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
}

Returns Playwright’s ARIA snapshot (YAML text) of the tab’s top document, with [ref=...] element references and a snapshotId. Use the refs as {ref, snapshotId} targets in /action and /wait.

NameInTypeRequiredDescription
browser_idquerystringNoSelects the instance: SDK and CLI clients send the request to the browser-<N> service host. A value that conflicts with the host’s instance is refused with 400 INSTANCE_SELECTOR_CONFLICT. Against a bare server it selects nothing; address instances there with browser_host + browser_port.
instanceGenerationquerystringNoThe instance generation from /metadata. If it no longer matches the running instance, returns 409 INSTANCE_CHANGED (also when no instance exists: details.expected is null). Repeating this key in the query string is a 400 VALIDATION_ERROR (instanceGeneration must not be repeated).
tabIdqueryintegerNoThe ID of the tab to snapshot (from /tabs). Omitted: the active tab. Supplied: exactly that tab; an unknown id returns 404 TAB_NOT_FOUND and a malformed id returns 400 VALIDATION_ERROR. Repeating this key in the query string is a 400 VALIDATION_ERROR (tabId must not be repeated).
timeoutMsqueryintegerNoBudget for the whole browser operation in milliseconds (default 10000), starting at handler entry (instance provisioning is never inside it). Repeating this key in the query string is a 400 VALIDATION_ERROR (timeoutMs must not be repeated).
maxCharsqueryintegerNoHard cut on a line boundary; truncated reports it. A truncated excerpt may not parse as standalone YAML. Default 30000. Repeating this key in the query string is a 400 VALIDATION_ERROR (maxChars must not be repeated).
includeValuesquerybooleanNoInclude the contents of form controls in the snapshot. Default false. Set to true only when the values are known not to be sensitive. Anything other than true or false returns 400, and repeating this key in the query string is a 400 VALIDATION_ERROR (includeValues must not be repeated).
Terminal window
curl -X GET "https://{projectId}-{containerId}-browser-1.{server}.containers.hoody.com/api/v1/browser/snapshot?maxChars=30000&includeValues=false"
{
"instanceGeneration": "gen-7",
"tabId": 2,
"elapsedMs": 184,
"snapshotId": "snap-7-3",
"url": "https://example.com/",
"title": "Example Domain",
"format": "aria-yaml",
"snapshot": "- banner:\n - heading \"Example Domain\" [ref=e1]\n - paragraph: \"Reserved for documentation examples.\" [ref=e2]\n- main:\n - text: \"More content\" [ref=e3]",
"truncated": false,
"chars": 142
}

Performs a native element action: click, fill, type, press, select, check, or hover on a target. A 200 means the browser operation completed, not that the website’s transaction succeeded; observe with /wait and /snapshot.

NameInTypeRequiredDescription
browser_idquerystringNoSelects the instance: SDK and CLI clients send the request to the browser-<N> service host. A value that conflicts with the host’s instance is refused with 400 INSTANCE_SELECTOR_CONFLICT. Against a bare server it selects nothing; address instances there with browser_host + browser_port.
NameTypeRequiredDescription
actionstringYesOne of click, fill, type, press, select, check, hover.
targetobjectYesElement target. Exactly one of ref, selector, role, label, text, placeholder, testId must be set (otherwise 400). name is allowed only with role; snapshotId is required with ref; exact is allowed only with role, label, text, or placeholder (default true). Every target is strict: more than one match returns 409 AMBIGUOUS_TARGET. Semantic and CSS targets address the top document only; use a ref to act inside an iframe. Refs are opaque tokens: a prefix like f2 is Playwright’s frame-sequence namespace and says nothing about iframes. Pass refs back verbatim.
instanceGenerationstringNoFrom /metadata; mismatch returns 409 INSTANCE_CHANGED.
tabIdintegerNoTab to act on (from /tabs); omitted uses the active tab, unknown returns 404 TAB_NOT_FOUND.
timeoutMsintegerNoBudget for the whole browser operation in milliseconds (1 to 30000, default 10000). The body overrides the query.
dialogobjectNoOptional DialogPolicy. Authorizes handling of one dialog raised during this action; it never requires one (no dialog returns dialog: null). A dialog that does not match, or a second dialog, is dismissed and the action fails with 409 UNEXPECTED_DIALOG. Fields: type (alert, confirm, prompt, beforeunload), response (accept, dismiss), promptText (text to enter when accepting a prompt; never echoed), message (if set, the dialog’s message must equal this exactly to be handled).
valuestringNofill/type: the text. Never echoed.
delayMsintegerNotype: delay between keys, in milliseconds (0 to 1000, default 0). Code points multiplied by delayMs must be smaller than timeoutMs (otherwise 400).
keystringNopress: Playwright key syntax (e.g. Enter, Control+a). Unknown returns 400.
valuesarray of stringNoselect: option values or labels (first match wins). No match returns 504 with details.availableOptions.
checkedbooleanNocheck: desired state (idempotent).
buttonstringNoclick: mouse button. One of left, middle, right (default left).
clickCountintegerNoclick: number of clicks (1 to 3, default 1).
modifiersarray of stringNoclick: keyboard modifiers (Alt, Control, Meta, Shift).
Terminal window
curl -X POST "https://{projectId}-{containerId}-browser-1.{server}.containers.hoody.com/api/v1/browser/action" \
-H "Content-Type: application/json" \
-d '{
"action": "click",
"target": {
"ref": "e12",
"snapshotId": "snap-7-3"
},
"timeoutMs": 10000
}'
{
"instanceGeneration": "gen-7",
"tabId": 2,
"elapsedMs": 88,
"action": "click",
"url": "https://example.com/next",
"dialog": null
}

Blocks until a wait condition holds or the budget is spent. A miss returns 504 TIMEOUT (phase wait), never matched:false.

NameInTypeRequiredDescription
browser_idquerystringNoSelects the instance: SDK and CLI clients send the request to the browser-<N> service host. A value that conflicts with the host’s instance is refused with 400 INSTANCE_SELECTOR_CONFLICT. Against a bare server it selects nothing; address instances there with browser_host + browser_port.
NameTypeRequiredDescription
conditionobjectYesDiscriminated by kind. target: {target, state} (state is attached, detached, visible, hidden, enabled, or disabled; detached and hidden are satisfied by absence). text: {target, text, match?} where match is contains (default) or equals; text is whitespace-normalized and case-sensitive, and innerText does not read an input’s value. url: exactly one of {url} (absolute, matched as normalized href) or {urlPrefix} (verbatim startsWith on href). loadState: {state} where state is domcontentloaded or load.
instanceGenerationstringNoFrom /metadata; mismatch returns 409 INSTANCE_CHANGED.
tabIdintegerNoTab to act on (from /tabs); omitted uses the active tab, unknown returns 404 TAB_NOT_FOUND.
timeoutMsintegerNoBudget for the whole browser operation in milliseconds (1 to 30000, default 10000). The body overrides the query.
Terminal window
curl -X POST "https://{projectId}-{containerId}-browser-1.{server}.containers.hoody.com/api/v1/browser/wait" \
-H "Content-Type: application/json" \
-d '{
"condition": {
"kind": "loadState",
"state": "load"
},
"timeoutMs": 15000
}'
{
"instanceGeneration": "gen-7",
"tabId": 2,
"elapsedMs": 1024,
"matched": true,
"url": "https://example.com/loaded"
}