# run Source: https://docs.narada.ai/api-reference/agent Execute automation tasks using the Narada Python SDK `Agent.run()` executes a natural-language task in a Narada environment and returns an `AgentResponse`. Create an environment first, then bind an agent to it: ```python theme={null} from narada import Agent, BrowserEnvironment env = BrowserEnvironment() agent = Agent(environment=env) response = await agent.run(prompt="Search for Narada AI and summarize the first result.") print(response.text) ``` `Agent.run()` is the current SDK interface. The older `Narada().open_and_initialize_browser_window()` and `window.agent(...)` pattern is no longer the recommended API. ## Constructor ```python theme={null} class Agent: def __init__( self, *, environment: Environment, kind: AgentKind | str = AgentKind.OPERATOR, ) -> None: ... ``` ## Run Signature ```python theme={null} async def run( self, prompt: str, *, reasoning: ReasoningEffort | None = None, clear_chat: bool | None = None, generate_gif: bool | None = None, output_schema: type[BaseModel] | None = None, previous_request_id: str | None = None, chat_history: list[RemoteDispatchChatHistoryItem] | None = None, additional_context: dict[str, str] | None = None, attachment: File | IO[Any] | None = None, time_zone: str = "America/Los_Angeles", user_resource_credentials: UserResourceCredentials | None = None, mcp_servers: list[McpServer] | None = None, secret_variables: dict[str, str] | None = None, input_variables: Mapping[str, Any] | None = None, callback_url: str | None = None, callback_secret: str | None = None, callback_headers: Mapping[str, Any] | None = None, on_input_required: InputRequiredCallback | None = None, critic: CriticConfig | None = None, timeout: int = 1000, ) -> AgentResponse ``` ## Agent Kinds Choose the agent kind when constructing `Agent`. The agent to use for task execution. Best for browser automation tasks like clicking, navigating, filling forms, and extracting data from pages. ```python theme={null} from narada import Agent, AgentKind agent = Agent(environment=env, kind=AgentKind.OPERATOR) ``` Best for read-only reasoning, answering questions about page content, and conversation-style tasks. ```python theme={null} from narada import Agent, AgentKind agent = Agent(environment=env, kind=AgentKind.CORE_AGENT) ``` Best for general productivity and non-browser-specific tasks. ```python theme={null} from narada import Agent, AgentKind agent = Agent(environment=env, kind=AgentKind.PRODUCTIVITY) ``` Invoke custom agents created in [Agent Studio](https://app.narada.ai/agent-studio) using the namespaced format. ```python theme={null} # Use $USER shorthand for your own account. agent = Agent(environment=env, kind="/$USER/my-data-analyst") # Or reference a shared agent explicitly. agent = Agent(environment=env, kind="/jane@company.com/sales-report-generator") ``` The same syntax works in the Narada chat side panel: ```text theme={null} /$USER/my-agent Search for recent tech news /jane@company.com/research-bot Find papers on transformers ``` Agents must be shared or published in [Agent Studio](/documentation/agent-studio) before other users can invoke them. ## Parameters The natural-language instruction for the task. When using `AgentKind.OPERATOR`, Narada applies the correct Operator prefix automatically. ```python theme={null} prompt="Search for machine learning jobs and extract the first 5 results" ``` Controls how much reasoning the Core Agent uses before responding. This option is only valid with `AgentKind.CORE_AGENT`. ```python theme={null} from narada import AgentKind, ReasoningEffort agent = Agent(environment=env, kind=AgentKind.CORE_AGENT) response = await agent.run( prompt="Analyze the page and explain the pricing model.", reasoning=ReasoningEffort.MEDIUM, ) ``` Whether to clear chat context before executing the command. ```python theme={null} clear_chat=True ``` Whether to capture an animated GIF of the automation trajectory. ```python theme={null} generate_gif=True ``` A Pydantic model class defining the expected structured response. ```python theme={null} from pydantic import BaseModel, Field class JobListing(BaseModel): title: str = Field(description="Job title") company: str = Field(description="Company name") location: str = Field(description="Job location") response = await agent.run( prompt="Extract the first job listing.", output_schema=JobListing, ) ``` The prior request ID to continue a conversation. ```python theme={null} first = await agent.run(prompt="Pick a lucky number.") second = await agent.run( prompt="What number did you pick?", previous_request_id=first.request_id, ) ``` A file-like object to attach to the request. The SDK uploads the file automatically before dispatching the task. ```python theme={null} with open("document.pdf", "rb") as f: response = await agent.run( prompt="Summarize the attached document.", attachment=f, ) ``` The time zone for time-related operations. ```python theme={null} time_zone="America/New_York" ``` MCP servers to connect during task execution. ```python theme={null} from narada_core.models import AuthenticationNone, McpServer mcp_server = McpServer( url="https://your-mcp-server.example.com", label="My MCP Server", description="Custom tools", authentication=AuthenticationNone(), ) response = await agent.run( prompt="Use the MCP tools to process this data.", mcp_servers=[mcp_server], ) ``` Sensitive values substituted at action time. The LLM sees placeholders such as `${password}`, not the actual values. See [Secrets and Secret Variables](/documentation/secrets-and-secret-variables#sdk-and-rest-secret-variables). ```python theme={null} import os response = await agent.run( prompt="Log in with username ${username} and password ${password}.", secret_variables={ "username": os.environ["APP_USERNAME"], "password": os.environ["APP_PASSWORD"], }, ) ``` Values passed into prompts using `{{$variable_name}}` syntax. File-like values are uploaded automatically. ```python theme={null} response = await agent.run( prompt="Summarize {{$doc}}.", input_variables={"doc": file_obj}, ) ``` URL to call when the task status changes or completes. Use this with `callback_secret` or `callback_headers` to authenticate callbacks. Callback invoked when a task enters an input-required state. Optional critic configuration that validates the run after the main agent finishes. Maximum time in seconds to wait for task completion. ```python theme={null} timeout=180 ``` ## Response `Agent.run()` returns an `AgentResponse` object: Unique identifier for this request. Use it for debugging, callbacks, or conversation continuation. Execution status: `"success"`, `"error"`, or `"input-required"`. The text response from the agent. Parsed structured data when `output_schema` is provided. Otherwise `None`. Discriminated union containing either text or structured data: ```typescript theme={null} { type: "text", content: string } | { type: "structured", content: object } ``` Usage metrics: * `actions`: Number of actions performed * `credits`: Credits consumed for the task Detailed log of agent actions. Top-level action trace entries include `start_ts`, `end_ts`, and `duration_ms` fields. See [Action Trace](/documentation/action-trace) for the trace format and timing semantics. Workflow-level trace data when available. Result from the optional critic step. ## Examples ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment async def main() -> None: env = BrowserEnvironment() agent = Agent(environment=env) try: response = await agent.run( prompt="Search for Python tutorials on Google and get the title of the first result", ) print(f"Status: {response.status}") print(f"Result: {response.text}") print(f"Actions used: {response.usage.actions}") finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment from pydantic import BaseModel, Field class SearchResult(BaseModel): query: str = Field(description="The search query used") result_count: str = Field(description="Number of results found") top_result_title: str = Field(description="Title of the first result") top_result_url: str = Field(description="URL of the first result") async def main() -> None: env = BrowserEnvironment() agent = Agent(environment=env) try: response = await agent.run( prompt="Search for AI research papers and extract search details", output_schema=SearchResult, ) data = response.structured_output assert data is not None print(f"Query: {data.query}") print(f"Results: {data.result_count}") print(f"Top result: {data.top_result_title}") print(f"URL: {data.top_result_url}") finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` ```python theme={null} import asyncio from narada import Agent, AgentKind, BrowserEnvironment async def main() -> None: env = BrowserEnvironment() agent = Agent(environment=env, kind=AgentKind.CORE_AGENT) try: response = await agent.run( prompt="Pick a lucky number between 1 and 100 for me.", ) print("Agent:", response.text) response = await agent.run( prompt="What did you pick again?", previous_request_id=response.request_id, ) print("Agent:", response.text) response = await agent.run( prompt="What's double that number?", previous_request_id=response.request_id, ) print("Agent:", response.text) finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment, NaradaTimeoutError async def main() -> None: env = BrowserEnvironment() agent = Agent(environment=env) try: max_attempts = 3 for attempt in range(max_attempts): try: response = await agent.run( prompt="Search for complex data analysis tutorials and summarize the top 3 results", timeout=60, generate_gif=True, ) print("Task completed successfully!") print(response.text) break except NaradaTimeoutError: if attempt == max_attempts - 1: raise print(f"Attempt {attempt + 1} timed out, retrying...") await agent.reset_agent_state() finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` ```python theme={null} import asyncio from narada import Agent, AgentKind, BrowserEnvironment from pydantic import BaseModel, Field class InvoiceData(BaseModel): invoice_number: str = Field(description="Invoice number") date: str = Field(description="Invoice date") vendor_name: str = Field(description="Vendor or supplier name") total_amount: str = Field(description="Total amount with currency") line_items: list[str] = Field(description="List of items or services") async def main() -> None: env = BrowserEnvironment() agent = Agent(environment=env, kind=AgentKind.CORE_AGENT) try: with open("/path/to/invoice.pdf", "rb") as f: response = await agent.run( prompt="Extract the invoice details from the attached document.", attachment=f, output_schema=InvoiceData, ) invoice = response.structured_output assert invoice is not None print(f"Invoice: {invoice.invoice_number}") print(f"Date: {invoice.date}") print(f"Vendor: {invoice.vendor_name}") print(f"Total: {invoice.total_amount}") print(f"Items: {', '.join(invoice.line_items)}") finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment async def main() -> None: env = BrowserEnvironment() agent = Agent(environment=env, kind="/$USER/my-research-agent") try: response = await agent.run( prompt="Search for recent machine learning papers.", ) print(response.text) finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` ## Best Practices Create one environment and reuse it across multiple `Agent` instances when tasks should share a browser session. Use `AgentKind.OPERATOR` for browser automation and `AgentKind.CORE_AGENT` for read-only reasoning or conversation. Define Pydantic schemas for consistent, typed responses from data extraction tasks. Use `secret_variables` or Agent Studio [Secrets and Secret Variables](/documentation/secrets-and-secure-variables) for passwords, API keys, or personal information that should not be exposed to the LLM. Pass file-like objects with `attachment=` instead of calling a separate upload method. Set appropriate timeouts and call `agent.reset_agent_state()` before retrying after a timeout. ## Migration from Older SDKs Older SDK examples used a Narada client and window object: ```python theme={null} async with Narada() as narada: window = await narada.open_and_initialize_browser_window() response = await window.agent(prompt='search for "jobs" on Google') ``` Use an environment and agent instead: ```python theme={null} env = BrowserEnvironment() agent = Agent(environment=env) try: response = await agent.run(prompt='search for "jobs" on Google') print(response.text) finally: await env.close() ``` For browser actions, call methods on `agent`: ```python theme={null} await agent.go_to_url(url="https://example.com") await agent.agentic_selector( action={"type": "click"}, selectors={"aria_label": "Submit"}, fallback_operator_query="click the Submit button", ) ``` For lifecycle and IDs, use the environment: ```python theme={null} await env.start() print(env.browser_window_id) await env.close() ``` # agentic_matching_selectors_finder Source: https://docs.narada.ai/api-reference/agentic-matching-selectors-finder Find all visible page elements matching a natural-language prompt and return reusable selector objects The `agentic_matching_selectors_finder` method uses a narrow Operator agent to identify every visible element that matches a prompt, then returns `AgenticSelectors` objects you can pass directly to `agentic_selector`. Use it when a workflow needs to loop over repeated controls, cards, rows, links, or accordion buttons and perform the same deterministic action on each match. This method finds matching targets only. To click, fill, read, or hover over the returned elements, iterate through the selector objects and call [`agentic_selector()`](/api-reference/agentic-selector). ## Method Signature ```python theme={null} async def agentic_matching_selectors_finder( self, *, prompt: str, timeout: int | None = 300, ) -> list[AgenticSelectors] ``` ## Parameters Natural-language description of the visible elements to find. Include the page section or container when possible so the finder returns the intended repeated targets. ```python theme={null} prompt='Find all visible "More details" buttons in the Highlights section' prompt="Find all visible down-arrow expand buttons in the accordion" prompt="Find all visible row action menus in the Open invoices table" ``` Maximum time in seconds to wait. The default is 300 seconds because this method may run an Operator agent. ## Return Value Returns a list of `AgenticSelectors` dictionaries. If no elements match, the method returns an empty list. Each returned selector object may include one or more selector fields: ```python theme={null} { "id": "element-id", "data_testid": "test-id", "name": "input-name", "aria_label": "Button label", "role": "button", "type": "submit", "text_content": "More details", "tag_name": "button", "class_name": "btn-primary", "dom_path": "html > body > div > button", "xpath": "//button[normalize-space()='More details']" } ``` ## Example ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: await agent.go_to_url(url="https://example.com/highlights") detail_button_selectors = await agent.agentic_matching_selectors_finder( prompt='Find all visible "More details" buttons in the Highlights section', ) print(f"Found {len(detail_button_selectors)} buttons") for index, selectors in enumerate(detail_button_selectors, start=1): await agent.agentic_selector( action={"type": "click"}, selectors=selectors, fallback_operator_query=( f'Click "More details" button #{index} in the Highlights section' ), ) await asyncio.sleep(1) finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` ## When to Use This vs agentic\_selector | | `agentic_matching_selectors_finder` | `agentic_selector` | | --------------- | ----------------------------------- | --------------------------------------- | | **Purpose** | Find many matching targets | Act on one target | | **Returns** | `list[AgenticSelectors]` | `AgenticSelectorResponse` | | **Best for** | Repeated controls or rows | Clicking, filling, reading, or hovering | | **Typical use** | Collect once, then loop | Execute each deterministic action | # agentic_mouse_action Source: https://docs.narada.ai/api-reference/agentic-mouse-action Perform mouse actions at recorded coordinates with Operator agent fallback The `agentic_mouse_action` method performs mouse actions at specific screen coordinates captured during a recording. If the action fails at those coordinates (e.g., due to page layout changes), it falls back to the Operator agent using a natural language query. This method is primarily used by agents generated through [Imitation Learning](/documentation/imitation-learning), where click coordinates are captured during recording. For most manual automations, prefer [`agentic_selector()`](/api-reference/agentic-selector) which targets elements by CSS selectors. ## Method Signature ```python theme={null} async def agentic_mouse_action( self, *, action: AgenticMouseAction, recorded_click: RecordedClick, fallback_operator_query: str, resize_window: bool = True, timeout: int | None = 60, ) -> None ``` ## Parameters The mouse action to perform. Supported actions: ```python theme={null} {"type": "click"} # Left click {"type": "right_click"} # Right click {"type": "double_click"} # Double click {"type": "fill", "text": "hello", "press_enter": False} # Type text at the location {"type": "scroll", "horizontal": 0, "vertical": 300} # Scroll by pixel offset ``` The coordinates and viewport dimensions from when the action was originally recorded. ```python theme={null} { "x": 450, # X coordinate in pixels "y": 320, # Y coordinate in pixels "viewport": { "width": 1920, # Viewport width when recorded "height": 1080 # Viewport height when recorded } } ``` Natural language instruction for the Operator agent to use if the coordinate-based action fails. ```python theme={null} fallback_operator_query="click the Submit button at the bottom of the form" ``` Whether to resize the browser window to match the recorded viewport dimensions before performing the action. This improves accuracy when the page layout is responsive. Maximum time in seconds to wait for the operation to complete. ## Return Value This method returns `None` and completes when the action has been performed successfully. ## Example ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: await agent.go_to_url(url="https://www.google.com") # Click at recorded coordinates, with Operator fallback await agent.agentic_mouse_action( action={"type": "click"}, recorded_click={ "x": 600, "y": 450, "viewport": {"width": 1440, "height": 900} }, fallback_operator_query='click the "Google Search" button' ) # Type into a field at recorded coordinates await agent.agentic_mouse_action( action={"type": "fill", "text": "Narada AI", "press_enter": True}, recorded_click={ "x": 600, "y": 350, "viewport": {"width": 1440, "height": 900} }, fallback_operator_query='type "Narada AI" in the search box and press Enter' ) finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` ## When to Use This vs agentic\_selector | | `agentic_mouse_action` | `agentic_selector` | | --------------- | -------------------------------- | -------------------------------------- | | **Targets by** | Screen coordinates | CSS selectors / element attributes | | **Best for** | Replaying recorded actions | Programmatic element targeting | | **Resilience** | Depends on page layout stability | Depends on element attribute stability | | **Typical use** | Generated by Imitation Learning | Written manually or in custom agents | Both methods fall back to the Operator agent when their primary targeting strategy fails, ensuring robust automation even when pages change. # agentic_selector Source: https://docs.narada.ai/api-reference/agentic-selector Perform actions on web elements using CSS selectors with Operator agent fallback The `agentic_selector` method attempts to perform actions on web elements using traditional CSS selectors first. If the selectors don't produce a unique element, it falls back to using the Operator agent to perform the action intelligently. This method combines the speed of traditional selectors with the reliability of AI agents. Use it when you want fast, precise element targeting with intelligent fallback for dynamic pages. ## Method Signature ```python theme={null} async def agentic_selector( self, *, action: AgenticSelectorAction, selectors: AgenticSelectors, fallback_operator_query: str, timeout: int | None = 300, ) -> AgenticSelectorResponse ``` ## Parameters The action to perform on the element. Supported actions: ```python theme={null} {"type": "click"} # Click the element {"type": "right_click"} # Right-click the element {"type": "double_click"} # Double-click the element {"type": "hover"} # Hover over the element {"type": "fill", "value": "text to enter"} # Fill input with text {"type": "select_option_by_index", "value": 0} # Select dropdown option by index {"type": "select_option_by_value", "value": "option_1"} # Select dropdown option by value {"type": "get_text"} # Get the element's text content {"type": "get_property", "property_name": "href"} # Get an element property ``` CSS selectors to identify the target element. Available selector types: ```python theme={null} { "id": "element-id", "data_testid": "test-id", "name": "input-name", "aria_label": "Button label", "role": "button", "type": "submit", "text_content": "Click me", "tag_name": "button", "class_name": "btn-primary", "dom_path": "html > body > div > button", "xpath": "//button[@id='submit']" } ``` Natural language instruction for the Operator agent to use if selectors fail to find a unique element. ```python theme={null} fallback_operator_query='click on "Images" near the top of the page' fallback_operator_query='fill in the email field with test@example.com' ``` Maximum time in seconds to wait for the operation to complete. Default is 300 seconds since the Operator agent may need extra time when falling back. ```python theme={null} timeout=60 # Wait up to 60 seconds timeout=None # Use default timeout (300 seconds) ``` ## Return Value Returns an `AgenticSelectorResponse` object: * For `get_text` and `get_property` actions: `response.value` contains the extracted string. * For all other actions (click, fill, hover, etc.): `response.value` is `None`. The method completes when the action has been performed successfully. ```python theme={null} # Extract text from an element response = await agent.agentic_selector( action={"type": "get_text"}, selectors={"data_testid": "price-display"}, fallback_operator_query="get the price text from the product card" ) print(f"Price: {response.value}") # e.g., "$29.99" ``` ## Example ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: # Navigate to Google await agent.go_to_url(url="https://www.google.com") # Try to click using aria-label, fall back to Operator if needed await agent.agentic_selector( action={"type": "click"}, selectors={"aria_label": "Search for Images "}, fallback_operator_query='click on "Images" near the top of the page' ) # Fill a search box await agent.agentic_selector( action={"type": "fill", "value": "python programming"}, selectors={"name": "q"}, fallback_operator_query='type "python programming" in the search box' ) finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` # close Source: https://docs.narada.ai/api-reference/close Gracefully close the current browser environment The environment `close` method gracefully closes the current browser environment. For local browser environments, you can also access the browser process ID and terminate the entire browser application. ## Method Signature ```python theme={null} async def close( self, *, timeout: int | None = None, ) -> None ``` ## Parameters Maximum time in seconds to wait for the environment to close. If `None`, uses the default system timeout. ```python theme={null} timeout=5 # Wait up to 5 seconds timeout=None # Use default timeout ``` ## Return Value This method returns `None` and completes when the environment has been closed. ## Example ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: # Perform automation tasks response = await agent.run( prompt="Search for Python tutorials and open the first result" ) print(f"Task completed: {response.text}") finally: # Close this specific browser environment await env.close() if __name__ == "__main__": asyncio.run(main()) ``` ```python theme={null} import asyncio import os import signal from narada import Agent, BrowserEnvironment async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: # Perform automation tasks response = await agent.run( prompt="Search for Python tutorials and open the first result" ) print(f"Task completed: {response.text}") finally: await env.close() # Get the browser process ID (only available for locally launched browsers) pid = env.browser_process_id # Terminate the entire browser process if pid is not None: print(f"Terminating browser process with PID: {pid}") os.kill(pid, signal.SIGTERM) if __name__ == "__main__": asyncio.run(main()) ``` # get_full_html Source: https://docs.narada.ai/api-reference/get-full-html Retrieve the complete HTML content of the current page without internal annotations using the Python SDK The `get_full_html` method retrieves the complete HTML content of the current browser page without internal annotations, making it suitable for HTML analysis, content extraction, and page archiving. The HTML returned by this method has all internal annotations stripped, ensuring you get clean, production-ready HTML. ## Method Signature ```python theme={null} async def get_full_html( self, *, timeout: int | None = None, ) -> GetFullHtmlResponse ``` ## Parameters Maximum time in seconds to wait for the HTML retrieval to complete. If `None`, uses the default system timeout. ```python theme={null} timeout=30 # Wait up to 30 seconds timeout=None # Use default timeout ``` ## Return Value Returns a `GetFullHtmlResponse` object with the following structure: The complete HTML content of the current page as a string, with all internal annotations removed. ```python theme={null} # Example response response.html = "......" ``` ## Example ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: # Navigate to a page await agent.go_to_url(url="https://example.com") # Get the full HTML content response = await agent.get_full_html() # Save HTML to a file with open("page.html", "w", encoding="utf-8") as f: f.write(response.html) print("HTML content retrieved and saved") finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` # Get Response Source: https://docs.narada.ai/api-reference/get-response GET /fast/v2/remote-dispatch/responses/{requestId} Retrieve automation task status and results ```bash cURL theme={null} curl -X GET 'https://api.narada.ai/fast/v2/remote-dispatch/responses/req_abc123def456' \ -H 'x-api-key: YOUR_API_KEY' ``` ```javascript Node.js theme={null} const response = await fetch( `https://api.narada.ai/fast/v2/remote-dispatch/responses/${requestId}`, { headers: { "x-api-key": process.env.NARADA_API_KEY, }, } ); const data = await response.json(); console.log(data); // { "status": "success", "response": { "output": { "type": "text", "content": "Successfully clicked on Insurance header..." } }, "createdAt": "...", "completedAt": "..." } ``` ```python Python theme={null} import requests import os response = requests.get( f'https://api.narada.ai/fast/v2/remote-dispatch/responses/{request_id}', headers={'x-api-key': os.getenv('NARADA_API_KEY')} ) data = response.json() print(data) # { "status": "success", "response": { "output": { "type": "text", "content": "Successfully clicked on Insurance header..." } }, "createdAt": "...", "completedAt": "..." } ``` ```json 200 Pending theme={null} { "status": "pending", "response": null, "createdAt": "2024-12-19T00:00:00.000000+00:00", "completedAt": null } ``` ```json 200 Success theme={null} { "status": "success", "response": { "output": { "type": "text", "content": "Successfully clicked on Insurance header and navigated to the insurance section" }, "actionTrace": [ { "url": "https://example.com", "action": "Clicked the Insurance header", "startTs": "2026-07-20T17:00:00.000Z", "endTs": "2026-07-20T17:00:01.500Z", "durationMs": 1500 }, { "url": "https://example.com/insurance", "action": "Opened the Claims section", "startTs": "2026-07-20T17:00:02.000Z", "endTs": "2026-07-20T17:00:02.800Z", "durationMs": 800 }, { "url": "https://example.com/insurance", "action": "Done: Successfully clicked on Insurance header and navigated to the insurance section", "credits": 0.0, "startTs": "2026-07-20T17:00:03.000Z", "endTs": "2026-07-20T17:00:03.600Z", "durationMs": 600 } ] }, "createdAt": "2024-12-19T00:00:00.000000+00:00", "completedAt": "2024-12-19T00:00:30.000000+00:00" } ``` ```json 200 Error theme={null} { "status": "error", "response": null, "createdAt": "2024-12-19T00:00:00.000000+00:00", "completedAt": "2024-12-19T00:00:30.000000+00:00" } ``` ```json 200 Input Required theme={null} { "status": "input-required", "response": { "output": { "type": "text", "content": "The operation could not be completed without additional user input." } }, "createdAt": "2024-12-19T00:00:00.000000+00:00", "completedAt": null } ``` Use this endpoint to poll for the completion status and results of a previously submitted automation task. This is an alternative to using webhooks for receiving task results. ## Authorization Your Narada API key for authentication. Must be the same key used to create the original task. ## Path Parameters The unique identifier returned from the [Remote Dispatch API](/api-reference/remote-dispatch) when the task was created. ## Response Current status of the automation task. Possible values: * `"pending"` - Task is still running * `"success"` - Task completed successfully * `"error"` - Task failed with an error * `"input-required"` - Task requires additional user input to continue The browser window ID where the task was executed. `null` if not applicable. Task response data. `null` when status is `"pending"`. The agent's output. Either a text response or structured data, depending on whether `responseFormat` was provided: ```typescript theme={null} { type: "text", content: string } | { type: "structured", content: object } ``` A detailed log of every action the agent took during execution. See [Action Trace](/documentation/action-trace) for the complete format. Deprecated. Use `output.content` instead. Deprecated. Use `output.content` instead (when `output.type` is `"structured"`). See [Structured Output](/documentation/structured-output) for details. ISO 8601 timestamp when the task was initially created. ISO 8601 timestamp when the task completed. `null` if still pending. Usage statistics for the task. `null` if not yet available. Number of actions performed during execution. Credits consumed by the task. ## Response Handling Options This endpoint provides synchronous access to task results. For production applications, consider these alternatives: Receive real-time notifications when tasks complete. More efficient and scalable than polling. Complete polling implementation examples with error handling and best practices. **Rate Limiting**: Implement reasonable polling intervals (5-10 seconds) to avoid overwhelming the API. Use exponential backoff for production applications. ## Comparison: Polling vs Webhooks | Feature | Polling | Webhooks | | --------------------- | ------------------------ | ------------------- | | **Setup Complexity** | Low | Medium | | **Real-time Updates** | No (delayed by interval) | Yes | | **Resource Usage** | Higher (active requests) | Lower | | **Reliability** | Medium | High (with retries) | | **Firewall Friendly** | Yes | Requires open ports | # get_screenshot Source: https://docs.narada.ai/api-reference/get-screenshot Capture a screenshot of the current browser window and retrieve it as base64-encoded image data using the Python SDK The `get_screenshot` method captures a screenshot of the current browser window and returns it as base64-encoded image data along with metadata. Unlike asking `Agent.run()` to take a screenshot and save it to disk, this method returns the screenshot data programmatically. This method returns screenshot data directly in your code rather than saving it to disk. If you need screenshots saved automatically to your Downloads folder, use `Agent.run()` with a prompt like "take a screenshot of this page" instead. ## Method Signature ```python theme={null} async def get_screenshot( self, *, timeout: int | None = None, ) -> GetScreenshotResponse ``` ## Parameters Maximum time in seconds to wait for the screenshot capture to complete. If `None`, uses the default system timeout. ```python theme={null} timeout=30 # Wait up to 30 seconds timeout=None # Use default timeout ``` ## Return Value Returns a `GetScreenshotResponse` object with the following structure: The screenshot image encoded as a base64 string. This can be decoded and saved to a file, displayed, or processed programmatically. A suggested filename for the screenshot, typically in a format like "Screenshot 2026-01-14 at 10.54.54 AM". The MIME type of the image format (e.g., "image/png", "image/jpeg"). ISO 8601 formatted timestamp indicating when the screenshot was captured. ## Example ```python theme={null} import asyncio import base64 from narada import Agent, BrowserEnvironment async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: # Navigate to a page await agent.go_to_url(url="https://example.com") # Capture a screenshot response = await agent.get_screenshot() # Decode and save the screenshot image_data = base64.b64decode(response.base64_content) extension = response.mime_type.split('/')[1] # "png" or "jpeg" filename = f"{response.name}.{extension}" with open(filename, "wb") as f: f.write(image_data) print(f"Screenshot saved as {filename}") print(f"Captured at: {response.timestamp}") finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` # get_simplified_html Source: https://docs.narada.ai/api-reference/get-simplified-html Retrieve simplified, processed HTML content optimized for content analysis using the Python SDK The `get_simplified_html` method retrieves a simplified version of the current page's HTML that has been processed by Narada to optimize it for content extraction and analysis. Unlike `get_full_html`, this method returns HTML that has been processed and simplified by Narada, removing unnecessary elements and optimizing the structure for content analysis. ## Method Signature ```python theme={null} async def get_simplified_html( self, *, timeout: int | None = None, ) -> GetSimplifiedHtmlResponse ``` ## Parameters Maximum time in seconds to wait for the simplified HTML retrieval to complete. If `None`, uses the default system timeout. ```python theme={null} timeout=30 # Wait up to 30 seconds timeout=None # Use default timeout ``` ## Return Value Returns a `GetSimplifiedHtmlResponse` object with the following structure: The simplified HTML content of the current page as a string, processed and optimized by Narada's backend for content analysis. ```python theme={null} # Example response response.html = "

Title

Content...

" ```
## Example ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: # Navigate to a page await agent.go_to_url(url="https://example.com/article") # Get simplified HTML for content analysis response = await agent.get_simplified_html() # Extract text content from simplified HTML from bs4 import BeautifulSoup soup = BeautifulSoup(response.html, 'html.parser') text_content = soup.get_text(separator=' ', strip=True) print(f"Extracted text: {text_content[:200]}...") finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` # get_url Source: https://docs.narada.ai/api-reference/get-url Get the URL of the current active page in the browser window The `get_url` method retrieves the URL of the currently active page in the browser window. This is useful for verifying navigation, capturing the current state, or building conditional logic based on the page location. ## Method Signature ```python theme={null} async def get_url( self, *, timeout: int | None = None, ) -> GetUrlResponse ``` ## Parameters Optional timeout in milliseconds. If not specified, uses the default timeout. ## Return Value Returns a `GetUrlResponse` object: The URL of the currently active page. ## Example ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: # Navigate to a page await agent.go_to_url(url="https://www.google.com", new_tab=True) # Get the current URL response = await agent.get_url() print(f"Current URL: {response.url}") # Output: Current URL: https://www.google.com/ # Use in conditional logic await agent.run(prompt="Search for Narada AI") response = await agent.get_url() if "search" in response.url: print("Search results page loaded successfully") finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` ## Use Cases Confirm the browser navigated to the expected page after an action Branch your automation based on the current page URL Log the current URL at each step for debugging automation flows Capture the final URL after redirects or form submissions # go_to_url Source: https://docs.narada.ai/api-reference/go-to-url Navigate browser windows to specific URLs programmatically using the Narada Python SDK The `go_to_url` method provides a simple, reliable way to navigate browser windows to specific URLs programmatically. Use this method to set up automation workflows that need to start from specific web pages or navigate between different sites during task execution. The `go_to_url` method waits for the navigation to complete before returning. This makes it useful for setting up initial page states or navigating between pages in multi-step workflows. ## Method Signature ```python theme={null} async def go_to_url( self, *, url: str, new_tab: bool = False, timeout: int | None = None, ) -> None ``` ## Parameters The target URL to navigate to. Must be a valid HTTP or HTTPS URL. ```python theme={null} url="https://www.google.com" url="https://arxiv.org/list/cs.AI/recent" ``` Whether to open the URL in a new tab instead of navigating the current tab. ```python theme={null} new_tab=False # Navigate current tab (default) new_tab=True # Open in new tab ``` Maximum time in seconds to wait for navigation to complete. If `None`, uses the default system timeout. ```python theme={null} timeout=30 # Wait up to 30 seconds timeout=None # Use default timeout ``` ## Return Value This method returns `None` and completes when the navigation is successful. ## Example ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: # Navigate to a specific page await agent.go_to_url(url="https://www.google.com") # Now perform actions on the current page response = await agent.run( prompt="search for 'python automation' and get the first result" ) print(f"Result: {response.text}") finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` ## Common Use Cases **Navigate to starting pages** * Set up initial page state * Navigate to login pages * Access specific application sections * Load data entry forms **Compare across multiple sources** * Open comparison sites in new tabs * Gather data from different pages * Cross-reference information * Aggregate research findings **Process items one by one** * Navigate through search results * Visit each item in a list * Process form submissions * Follow workflow steps **Gather resources systematically** * Download files from multiple sources * Collect images or documents * Archive web content * Build resource libraries ## Best Practices Always use complete, valid URLs with protocol (https\://) for reliable navigation Set appropriate timeouts for slow-loading pages or unreliable network conditions Implement proper error handling for navigation failures and network issues Use `new_tab=True` strategically to keep important pages open for reference ## Error Handling The `go_to_url` method can raise the following exceptions: Raised when navigation exceeds the specified timeout period. ```python theme={null} from narada import NaradaTimeoutError try: await agent.go_to_url(url="https://slow-loading-site.com", timeout=10) except NaradaTimeoutError: print("Navigation timed out after 10 seconds") ``` Raised for general navigation errors (invalid URLs, network issues, etc.). ```python theme={null} from narada import NaradaError try: await agent.go_to_url(url="https://invalid-url") except NaradaError as e: print(f"Navigation failed: {e}") ``` ## Integration with Other Methods The `go_to_url` method works seamlessly with other agent methods: ```python theme={null} # Navigate then perform actions await agent.go_to_url(url="https://example.com") await agent.run(prompt="fill out the contact form") # Navigate then use selectors await agent.go_to_url(url="https://app.example.com") await agent.agentic_selector( action={"type": "click"}, selectors={"aria_label": "Login"}, fallback_operator_query="click the login button" ) # Navigate then read data await agent.go_to_url(url="https://sheets.google.com/spreadsheet/123") data = await agent.read_google_sheet( spreadsheet_id="123", range="A1:C10" ) ``` ## Performance Considerations The `go_to_url` method waits for the page to fully load before returning. For pages with heavy JavaScript or many resources, consider using appropriate timeout values to avoid long wait times. When working with multiple pages, consider opening them in new tabs rather than navigating back and forth, which can be more efficient for comparison tasks. # Introduction Source: https://docs.narada.ai/api-reference/introduction Get started with the Narada API quickly and efficiently # Narada API Welcome to the **Narada API** documentation. Narada is an AI-powered browser automation platform that enables you to automate web interactions, extract data, run custom agents, and perform complex workflows through simple API calls. ## Quick Overview The Narada API provides several endpoint groups: Send commands to launch and control browser automation tasks Poll for task completion and retrieve results Use the Python SDK for programmatic browser control with `Agent.run()`, `agentic_selector()`, and more Integrate via Agent-to-Agent protocol or Model Context Protocol ## What You Can Do * **Web Automation**: Navigate websites, fill forms, click buttons using natural language * **Data Extraction**: Extract structured data from any webpage using Pydantic models * **Custom Agents**: Build and run multi-step workflows in the [Agent Studio](/documentation/agent-studio) * **Cloud Browsers**: Run tasks in serverless [Cloud Browser Sessions](/documentation/cloud-browser-sessions) without a local Chrome * **MCP Integration**: Connect external tools via [MCP servers](/documentation/mcp-builder) * **File Operations**: Attach files, download documents, and capture screenshots * **Webhooks**: Receive real-time notifications when tasks complete # print_message Source: https://docs.narada.ai/api-reference/print-message Display messages in the Narada extension side panel chat using the Python SDK The `print_message` method displays custom messages in the Narada extension's side panel chat. This is different from Python's regular `print()` function - use this method when you want to show messages to end users viewing the chat or inject messages into the chat history for agents to read later. This method only displays messages in the Narada extension side panel chat. If you just want to print something to your Python console, use the regular `print()` function instead. ## Method Signature ```python theme={null} async def print_message( self, *, message: str, timeout: int | None = None, ) -> None ``` ## Parameters The text message to display in the side panel chat. ```python theme={null} message="Task completed successfully!" message="Processing item 3 of 10..." ``` Maximum time in seconds to wait for the message to be displayed. If `None`, uses the default system timeout. ```python theme={null} timeout=10 # Wait up to 10 seconds timeout=None # Use default timeout ``` ## Return Value This method returns `None` and completes when the message has been displayed. ## Example ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: # Show a message in the chat await agent.print_message(message="Starting data extraction...") # Perform automation await agent.go_to_url(url="https://example.com") response = await agent.run(prompt="extract the page title") # Show results in the chat await agent.print_message(message=f"Extracted title: {response.text}") finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` # prompt_for_user_input Source: https://docs.narada.ai/api-reference/prompt-for-user-input Prompt the user for workflow variable values in the Narada browser UI The `prompt_for_user_input` method shows a form in the Narada browser UI and waits for the user to provide values for one or more workflow variables. Use this when a Python agent needs structured input before it can continue. For a higher-level guide covering both human-in-the-loop methods, see [Human-in-the-Loop Steps](/documentation/human-in-the-loop). ## Method Signature ```python theme={null} async def prompt_for_user_input( self, *, step_id: str, variables: list[PromptForUserInputVariable], prompt_message: str | None = None, timeout: int | None = 300, ) -> dict[str, Any] ``` ## Parameters A stable identifier for this prompt step. ```python theme={null} step_id="collect-report-filters" ``` The variables to ask the user to fill. Each variable has: * `name`: variable name * `type`: one of `string`, `number`, `boolean`, `enum`, `dataTable`, `object`, or `array` * `required`: whether the user must provide a value * `enum_values`: allowed values for `enum` variables ```python theme={null} variables=[ {"name": "region", "type": "enum", "required": True, "enum_values": ["west", "east"]}, {"name": "max_accounts", "type": "number", "required": True}, {"name": "notes", "type": "string", "required": False}, ] ``` Text to show above the generated input fields. Use this to explain what the user should enter in plain language. ```python theme={null} prompt_message="Enter the Jira project where the requirements are located." ``` Maximum time in seconds to wait for the user response. The default is 300 seconds, and the maximum is 3600 seconds. ## Return Value Returns a dictionary keyed by variable name: ```python theme={null} { "region": "west", "max_accounts": 25, } ``` Optional variables may be omitted if the user leaves them blank. If the interaction is cancelled or aborted, the method raises `UserAbortedError`. For `dataTable` variables, returned values use the canonical table shape: ```python theme={null} { "columnNames": ["Name", "Email"], "rows": [ ["Ada Lovelace", "ada@example.com"], ["Grace Hopper", "grace@example.com"], ], } ``` ## Example ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment, UserAbortedError async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: try: values = await agent.prompt_for_user_input( step_id="collect-search-options", variables=[ {"name": "search_query", "type": "string", "required": True}, {"name": "max_results", "type": "number", "required": True}, { "name": "source", "type": "enum", "required": False, "enum_values": ["web", "crm", "docs"], }, ], prompt_message="Choose what to search for and how many results to return.", timeout=300, ) except UserAbortedError: print("The prompt was cancelled.") return response = await agent.run( prompt=( f"Search {values.get('source', 'web')} for " f"{values['search_query']} and return the top {values['max_results']} results." ) ) print(response.text) finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` # read_google_sheet Source: https://docs.narada.ai/api-reference/read-google-sheet Read data from Google Sheets using the Narada Python SDK The `read_google_sheet` method reads data from a specified range of cells in a Google Sheet. This method requires that the user has the appropriate Google Sheets permissions configured in their Narada extension. This method requires Google Sheets access to be configured in the Narada extension. If this is your first time using Google Sheets methods, the Narada extension will prompt you to authorize access. The user must have read permissions for the specified spreadsheet. ## Method Signature ```python theme={null} async def read_google_sheet( self, *, spreadsheet_id: str, range: str, timeout: int | None = None, ) -> ReadGoogleSheetResponse ``` ## Parameters The ID of the Google Sheet. This is the long string in the Google Sheets URL. ```python theme={null} # From URL: https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit spreadsheet_id="1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms" ``` The range of cells to read in A1 notation. Can include sheet name. ```python theme={null} range="A1:C10" # Range on default sheet range="Sheet1!A1:C10" # Range on specific sheet range="Data!B2:E20" # Named sheet with range ``` Maximum time in seconds to wait for the operation to complete. ```python theme={null} timeout=30 # Wait up to 30 seconds timeout=None # Use default timeout ``` ## Return Value Returns a `ReadGoogleSheetResponse` object with the following structure: A 2D array containing the cell values from the specified range. Each inner list represents a row. ```python theme={null} # Example response response.values = [ ["Name", "Age", "City"], ["Alice", "25", "New York"], ["Bob", "30", "San Francisco"] ] ``` ## Example ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: # Read data from a Google Sheet response = await agent.read_google_sheet( spreadsheet_id="1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms", range="Sheet1!A1:C10" ) # Access the data for row in response.values: print(row) finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` # Remote Dispatch Source: https://docs.narada.ai/api-reference/remote-dispatch POST /fast/v2/remote-dispatch Execute automation tasks remotely via the Narada API ```bash cURL theme={null} curl -X POST 'https://api.narada.ai/fast/v2/remote-dispatch' \ -H 'Content-Type: application/json' \ -H 'x-api-key: YOUR_API_KEY' \ -d '{ "prompt": "/Operator click on Insurance header", "browserWindowId": "your-browser-window-id", "clearChat": true }' ``` ```javascript Node.js theme={null} const response = await fetch("https://api.narada.ai/fast/v2/remote-dispatch", { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": process.env.NARADA_API_KEY, }, body: JSON.stringify({ prompt: "/Operator click on Insurance header", browserWindowId: "your-browser-window-id", clearChat: true, }), }); const data = await response.json(); console.log(data); // { "requestId": "req_abc123def456" } ``` ```python Python theme={null} import requests import os response = requests.post( 'https://api.narada.ai/fast/v2/remote-dispatch', headers={ 'Content-Type': 'application/json', 'x-api-key': os.getenv('NARADA_API_KEY') }, json={ 'prompt': '/Operator click on Insurance header', 'browserWindowId': 'your-browser-window-id', 'clearChat': True } ) data = response.json() print(data) # { "requestId": "req_abc123def456" } ``` ```json 202 Accepted theme={null} { "requestId": "req_abc123def456" } ``` ```json 401 Unauthorized theme={null} { "error": "Unauthorized", "message": "Invalid API key" } ``` ```json 404 Browser Window Not Found theme={null} { "error": "Browser Window Not Found", "message": "The specified browser window ID is not available" } ``` ```json 400 Bad Request theme={null} { "error": "Invalid Request", "message": "The prompt field is required and must start with /Operator" } ``` The Remote Dispatch API allows you to execute automation tasks remotely on browser windows with the Narada Chrome Extension installed, or in serverless [Cloud Browser Sessions](/documentation/cloud-browser-sessions). This endpoint returns immediately with a request ID for tracking, while the actual automation runs asynchronously. ## Authorization Your Narada API key for authentication. See [Authentication](/documentation/authentication) for how to obtain your key. Must be set to `application/json`. ## Body The natural language instruction for the task to execute. When using the Python SDK's `Agent.run()` method with the default Operator agent, the `/Operator` prefix is added automatically. When calling the REST API directly, prefix your prompt with `/Operator` to use the web automation agent, or omit it for the Generalist agent. Unique identifier for the specific browser window to target. Required for `"client"` execution mode. See [Authentication](/documentation/authentication) for more information. How the task should be executed. Possible values: * `"client"`: Runs in a browser window with the Narada extension installed (requires `browserWindowId`) * `"cloud"`: Runs server-side without a browser * `"cloud_browser"`: Creates a fresh [Cloud Browser Session](/documentation/cloud-browser-sessions) and runs the task in it (no extension needed) Whether to clear the chat history before executing the command. Set to `false` to continue a previous conversation. Link this request to a previous one for conversation continuity. When set, the agent retains context from the previous request. Whether to capture an animated GIF of the automation process. When enabled, the GIF is automatically saved to the `Narada Downloads` directory. Learn more about [Action Trace](/documentation/action-trace). Specify structured output format using JSON Schema. See [Structured Output documentation](/documentation/structured-output) for detailed examples. Must be set to `"jsonSchema"`. A valid JSON Schema object defining the expected response structure. File attachments to provide to the agent. Each attachment is an object with a `key` field (obtained from the file upload endpoint). ```json theme={null} "attachments": [{"key": "uploads/abc123/report.pdf"}] ``` The timezone for any time-related operations in the automation task. MCP (Model Context Protocol) servers to connect during task execution. Each server provides additional tools the agent can use. See [A2A and MCP](/documentation/a2a-and-mcp). Secret variables for prompt placeholders. Use `${variable_name}` syntax in your prompt. Values are substituted **after** the LLM plans its actions, so the LLM never sees the actual values. Ideal for passwords, API keys, and sensitive data. See [Secrets and Secret Variables](/documentation/secrets-and-secure-variables#sdk-and-rest-secret-variables). ```json theme={null} { "prompt": "Log in with username ${user} and password ${pass}", "secretVariables": {"user": "", "pass": ""} } ``` Input variables passed to custom agents. These are visible to the agent and accessible in custom agent workflows via the `variables` dictionary. See [Input Variables](/documentation/input-variables). Webhook URL to receive the response asynchronously when the task completes. See [Webhooks documentation](/documentation/webhooks). A secret key included in the webhook payload body for verification. Use this to validate that webhook requests are genuinely from Narada. HTTP headers to include when sending the webhook POST request to your `callbackUrl`. ```json theme={null} { "callbackHeaders": { "Authorization": "Bearer ", "X-Custom-Header": "value" } } ``` Only `Authorization` and headers starting with `x-` (case-insensitive) are allowed. Other headers will be rejected with a 422 error for security reasons. If `true`, the API blocks until the task completes and returns the full response inline (instead of returning just a `requestId`). Useful for simple scripts. Returns 408 if the task times out. Optional label for the cloud browser session. Only used when `executionMode` is `"cloud_browser"`. ## Response Unique identifier for tracking the request. Use this ID for: - Polling the status via [GET /responses/](/api-reference/get-response) - Correlating webhook responses - Debug and monitoring purposes The API returns `202 ACCEPTED` immediately while the automation task runs in the background. Use the `requestId` to track progress through polling or webhooks. ## Example Commands ```bash theme={null} # Simple navigation /Operator navigate to the reports section # Data extraction /Operator extract customer information from the current table # Multi-step workflow /Operator 1) Navigate to customer database, 2) Search overdue accounts, 3) Export to CSV # Conditional logic /Operator if error messages exist, screenshot them, otherwise extract user count # Cloud browser (no extension needed) # Set executionMode to "cloud_browser", no browserWindowId required ``` ## Best Practices **Command Prefix**: Prefix automation commands with `/Operator` to invoke the web agent. When using the Python SDK's `Agent.run()` method with the default Operator agent, this prefix is added automatically. **Descriptive Prompts**: Be specific about your intentions. Instead of `/Operator click button`, use `/Operator click the Submit button in the contact form`. * **Use Browser Window IDs** to target specific browser contexts when working with multiple windows * **Implement timeouts** for long-running automation tasks * **Use webhooks** for better performance with asynchronous processing * **Test with simple commands** before implementing complex workflows * **Enable GIF generation** for debugging complex automation sequences ## Rate Limiting API requests are subject to rate limiting based on your subscription plan. For high-volume usage or custom rate limits, contact [support@narada.ai](mailto:support@narada.ai). # File attachments Source: https://docs.narada.ai/api-reference/upload-file Attach files to agent requests The current Narada SDK uploads file attachments automatically when you pass a file-like object to `Agent.run(attachment=...)` or `input_variables={...}`. You do not need to call a separate public `upload_file()` method. Attachments are useful for document analysis, data extraction, and workflows where the agent needs to process file content. ## Method Signature ```python theme={null} async def run( self, prompt: str, *, attachment: File | IO[Any] | None = None, ... ) -> AgentResponse ``` ## Parameters A file-like object opened in binary read mode, or a `File` object returned by a lower-level upload flow. ```python theme={null} with open("/path/to/document.pdf", "rb") as f: response = await agent.run( prompt="Summarize the attached document.", attachment=f, ) ``` ## Return Value `Agent.run()` returns an `AgentResponse`. For text responses, read `response.text`. For structured output, read `response.structured_output`. ## Example ```python theme={null} import asyncio from narada import Agent, AgentKind, BrowserEnvironment async def main(): env = BrowserEnvironment() agent = Agent(environment=env, kind=AgentKind.CORE_AGENT) try: with open("/path/to/report.pdf", "rb") as f: response = await agent.run( prompt="Summarize the key points from the attached report.", attachment=f, ) print(f"Summary: {response.text}") finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` You can also pass file-like objects inside `input_variables`; the SDK uploads them before dispatching the request. # user_approval Source: https://docs.narada.ai/api-reference/user-approval Prompt the user to approve or reject an action in the Narada browser UI The `user_approval` method shows an approval card in the Narada browser UI and waits for the user to choose one of two buttons. Use this when a Python agent should not continue until a person explicitly approves or rejects the next action. For a higher-level guide covering both human-in-the-loop methods, see [Human-in-the-Loop Steps](/documentation/human-in-the-loop). ## Method Signature ```python theme={null} async def user_approval( self, *, step_id: str, prompt_message: str, approve_label: str, reject_label: str, timeout: int | None = 300, ) -> bool ``` ## Parameters A stable identifier for this approval step. ```python theme={null} step_id="approve-invoice-submission" ``` The message shown in the approval card. ```python theme={null} prompt_message="Submit this invoice for approval?" ``` The label for the button that returns `True`. ```python theme={null} approve_label="Submit" ``` The label for the button that returns `False`. ```python theme={null} reject_label="Cancel" ``` Maximum time in seconds to wait for the user response. The default is 300 seconds, and the maximum is 3600 seconds. ## Return Value `True` when the user clicks the approve button. `False` when the user clicks the reject button. If the interaction is cancelled or aborted, the method raises `UserAbortedError`. ## Example ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment, UserAbortedError async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: try: approved = await agent.user_approval( step_id="approve-send-message", prompt_message="Send the drafted message to the customer?", approve_label="Send", reject_label="Do not send", timeout=300, ) except UserAbortedError: print("The approval step was cancelled.") return if not approved: print("The user rejected the action.") return await agent.run(prompt="Send the drafted message.") finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` # write_google_sheet Source: https://docs.narada.ai/api-reference/write-google-sheet Write data to Google Sheets using the Narada Python SDK The `write_google_sheet` method writes data to a specified range of cells in a Google Sheet. This method requires that the user has the appropriate Google Sheets permissions configured in their Narada extension. This method requires Google Sheets access to be configured in the Narada extension. If this is your first time using Google Sheets methods, the Narada extension will prompt you to authorize access. The user must have write permissions for the specified spreadsheet. ## Method Signature ```python theme={null} async def write_google_sheet( self, *, spreadsheet_id: str, range: str, values: list[list[str]], timeout: int | None = None, ) -> None ``` ## Parameters The ID of the Google Sheet. This is the long string in the Google Sheets URL. ```python theme={null} # From URL: https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit spreadsheet_id="1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms" ``` The range of cells to write to in A1 notation. Can include sheet name. ```python theme={null} range="A1:C3" # Range on default sheet range="Sheet1!A1:C3" # Range on specific sheet range="Data!B2:E5" # Named sheet with range ``` A 2D array containing the data to write. Each inner list represents a row of data. ```python theme={null} values=[ ["Name", "Age", "City"], ["Alice", "25", "New York"], ["Bob", "30", "San Francisco"] ] ``` Maximum time in seconds to wait for the operation to complete. ```python theme={null} timeout=30 # Wait up to 30 seconds timeout=None # Use default timeout ``` ## Return Value This method returns `None` and completes when the data has been written to the sheet. ## Example ```python theme={null} import asyncio from narada import Agent, BrowserEnvironment async def main(): env = BrowserEnvironment() agent = Agent(environment=env) try: # Write data to a Google Sheet await agent.write_google_sheet( spreadsheet_id="1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms", range="Sheet1!A1:C3", values=[ ["Product", "Price", "Stock"], ["Laptop", "999", "15"], ["Mouse", "25", "50"] ] ) print("Data written successfully!") finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` # A2A and MCP Integration Source: https://docs.narada.ai/documentation/a2a-and-mcp Learn how to integrate with Narada using Agent-to-Agent (A2A) and Model Context Protocol (MCP) standards Narada supports two industry-standard protocols for agent integration: **Agent-to-Agent (A2A)** and **Model Context Protocol (MCP)**. These protocols enable seamless communication between Narada and other AI agents or systems. ## Overview Both A2A and MCP provide standardized ways to interact with Narada's capabilities: * **A2A Protocol**: Enables direct agent-to-agent communication following the A2A specification * **MCP Protocol**: Uses the Model Context Protocol with Streamable HTTP for structured interactions Direct agent-to-agent communication using the A2A specification Structured interactions using Model Context Protocol with Streamable HTTP ## A2A Protocol The Agent-to-Agent (A2A) protocol enables direct communication between AI agents using a standardized specification. ### Agent Discovery Narada's A2A agent information is available at the well-known endpoint. You can discover the agent card as : ```bash cURL theme={null} curl -X GET 'https://api.narada.ai/fast/v2/a2a/.well-known/agent.json' ``` ```json Agent Information theme={null} { "name": "Narada AI Agent", "description": "AI agent for automating tasks through web browsing and integrations", "version": "0.0.1", "url": "https://api.narada.ai/fast/v2/a2a", "authentication": { "type": "bearer", "description": "API key required" }, // Additional fields... } ``` ### A2A Endpoint The main A2A endpoint follows the exact A2A protocol specification: **Endpoint**: `https://api.narada.ai/fast/v2/a2a` The A2A endpoint implements the complete A2A protocol specification. Refer to the [official A2A documentation](https://a2a-protocol.org/latest/) for detailed protocol requirements and message formats. ### Authentication A2A requests require authentication using your Narada API key: Bearer token for API authentication. Format: `Bearer YOUR_API_KEY` ### Example A2A Request This example demonstrates how to use the A2A Python SDK to discover Narada's agent card and send a streaming message request asking the Operator agent to navigate to google. First, install the A2A Python SDK: ```bash theme={null} pip install a2a-sdk ``` The A2A SDK is the official Python library for the Agent2Agent Protocol. You can find more details in the [A2A Python SDK repository](https://github.com/a2aproject/a2a-python). ```python wrap expandable theme={null} import httpx from a2a.client import A2ACardResolver, A2AClient from a2a.types import ( MessageSendParams, MessageSendConfiguration, Message, TextPart, JSONRPCErrorResponse, SendStreamingMessageRequest, ) from uuid import uuid4 headers = { "Authorization": f"Bearer " } agent = "https://api.narada.ai/fast/v2/a2a" async with httpx.AsyncClient(timeout=30, headers=headers) as client: card_resolver = A2ACardResolver( client, base_url=agent, agent_card_path="/.well-known/agent.json" ) card = await card_resolver.get_agent_card() client = A2AClient(client, agent_card=card) # Create message message = Message( role="user", parts=[TextPart(text="/Operator navigate to google.com")], message_id=str(uuid4()), context_id=uuid4().hex, ) # Send streaming request response_stream = client.send_message_streaming( SendStreamingMessageRequest( id=str(uuid4()), params=MessageSendParams( id=str(uuid4()), message=message, configuration=MessageSendConfiguration(accepted_output_modes=["text"]), ), ) ) # Print streaming responses async for result in response_stream: if isinstance(result.root, JSONRPCErrorResponse): print(f"Error: {result.root.error}") else: event = result.root.result print(f"stream event => {event.model_dump_json(exclude_none=True)}") ``` ## MCP Protocol The Model Context Protocol (MCP) provides structured communication using Streamable HTTP for efficient data exchange. ### MCP Endpoint **Endpoint**: `https://api.narada.ai/fast/v2/mcp` The MCP endpoint implements the complete Model Context Protocol specification. Refer to the [official MCP documentation](https://modelcontextprotocol.io/docs/) for the detailed protocol requirements and message formats ### Authentication MCP requests use the same authentication method as A2A: Bearer token for API authentication. Format: `Bearer YOUR_API_KEY` ### Example MCP Request This example demonstrates how to use the FastMCP Python SDK to send a streaming message request asking the Operator agent to navigate to google. First, install the FastMCP Python SDK: ```bash theme={null} pip install fastmcp ``` FastMCP is a Python library for the Model Context Protocol. You can find more details in the [FastMCP documentation](https://gofastmcp.com/clients/client). ```python wrap expandable theme={null} from fastmcp import Client from fastmcp.client.transports import StreamableHttpTransport headers = { "Authorization": f"Bearer " } url = "https://api.narada.ai/fast/v2/mcp" client = Client( transport=StreamableHttpTransport( url, headers=headers, ), ) async with client: await client.ping() # Create a new task result = await client.call_tool( name="narada", arguments={ "prompt": "/Operator navigate to google.com" } ) print(result.content) ``` ## Error Handling Both protocols return standardized error responses: ```json Error Response theme={null} { "jsonrpc": "2.0", "id": 1, "error": { "code": -32601, "message": "Method not found", "data": // Some data... } } ``` ### Common Error Codes * **401 Unauthorized**: Invalid or missing API key * **400 Bad Request**: Invalid request format or parameters * **422 Unprocessable Entity**: Request validation failed * **500 Internal Server Error**: Unexpected server error * **503 Service Unavailable**: Service temporarily unavailable # Account Deletion Source: https://docs.narada.ai/documentation/account-deletion Learn how to permanently delete your Narada account ## Deleting Your Account If you need to delete your Narada account, you can do so directly from the settings page. Navigate to your account settings page in the Narada dashboard. Locate and click the **Delete Account** button to permanently remove your account. Settings page showing the delete account button location This action is permanent and cannot be undone. All your data, including saved prompts, custom instructions, and account history, will be permanently deleted. # Action Trace Source: https://docs.narada.ai/documentation/action-trace Detailed logging and visual recording of automation workflows Action tracing provides comprehensive visibility into your automation workflows through two complementary features: detailed step logging and visual GIF recording. ## Action Trace Response Action traces provide detailed step-by-step logs of automation execution, returned in API responses for both custom agent shortcuts and `/Operator` commands. ### Operator Traces Simple action logs for `/Operator` commands: ```json theme={null} { "actionTrace": [ { "url": "https://arxiv.org/", "action": "Typed 'artificial intelligence' and pressed Enter into search input", "startTs": "2026-07-20T17:00:00.000Z", "endTs": "2026-07-20T17:00:01.500Z", "durationMs": 1500 }, { "url": "https://arxiv.org/search/", "action": "Clicked on search button", "startTs": "2026-07-20T17:00:02.000Z", "endTs": "2026-07-20T17:00:02.800Z", "durationMs": 800 }, { "url": "https://arxiv.org/search/", "action": "Done: Found the requested results.", "credits": 0.0, "startTs": "2026-07-20T17:00:03.000Z", "endTs": "2026-07-20T17:00:03.600Z", "durationMs": 600 } ] } ``` Each top-level action trace item includes: | Field | Type | Description | | ------------ | ------- | ----------------------------------------------- | | `url` | string | Page URL associated with the action. | | `action` | string | Human-readable action description. | | `credits` | number | Credits charged for the action, when included. | | `startTs` | string | UTC ISO 8601 timestamp when the action started. | | `endTs` | string | UTC ISO 8601 timestamp when the action ended. | | `durationMs` | integer | Time taken for the action, in milliseconds. | These fields show how long each action took. In the Python SDK, these fields are available as `start_ts`, `end_ts`, and `duration_ms`: ```python theme={null} for item in response.action_trace or []: if hasattr(item, "duration_ms"): print(item.action, item.start_ts, item.end_ts, item.duration_ms) ``` ### Custom Agent Traces Detailed step logs for custom agent workflows with multiple action types: ```json theme={null} { "actionTrace": [ { "url": "https://example.com", "step_type": "goToUrl", "description": "Navigated to https://example.com" }, { "url": "https://example.com", "step_type": "agent", "agent_type": "operator", "action_trace": [...] } ] } ``` #### Supported Step Types Custom agent traces use a `step_type` field to identify the action. The full list of supported step types: | Step Type | Description | | ------------------------------------------------- | ----------------------------------------------------- | | `start` / `end` | Workflow boundaries | | `goToUrl` | Navigate to a URL | | `getUrl` | Get the current page URL | | `agent` | Sub-agent invocation (contains nested `action_trace`) | | `agenticSelector` | Element selection via CSS selectors with fallback | | `agenticMouseAction` | Mouse action at recorded coordinates | | `getFullHtml` / `getSimplifiedHtml` | HTML content capture | | `getScreenshot` | Screenshot capture | | `print` | Print a message to the chat | | `python` | Inline Python code execution | | `for` / `while` | Loop constructs | | `if` | Conditional branching | | `setVariable` | Variable assignment | | `wait` | Delay step | | `waitForElement` | Wait for an element to appear | | `pressKeys` | Keyboard input | | `readGoogleSheet` / `writeGoogleSheet` | Google Sheets operations | | `dataTableExportAsCsv` | Export data as CSV | | `objectExportAsJson` | Export data as JSON | | `dataTableInsertRow` / `dataTableUpdateCellValue` | Data table operations | | `objectSetProperties` | Object property updates | | `runCustomAgent` | Invoke another custom agent workflow | ### Formatting Example Convert action traces to readable markdown (handles multiple custom agent step types): ```python theme={null} def format_action_trace(trace_data): if not trace_data: return "No action trace available" markdown = "# Action Trace\n\n" for i, step in enumerate(trace_data, 1): if 'action' in step: # Operator trace duration = step.get('durationMs') suffix = f" ({duration} ms)" if duration is not None else "" markdown += f"{i}. **{step['url']}**\n - {step['action']}{suffix}\n\n" else: # Custom agent trace - handle different step types step_type = step['step_type'] description = step.get('description', step_type) if step_type == 'goToUrl': markdown += f"{i}. 🌐 **Navigate** to {step.get('url', 'URL')}\n" elif step_type == 'agent': agent_type = step.get('agent_type', 'unknown') markdown += f"{i}. 🤖 **Agent** ({agent_type}) - {description}\n" if step.get('action_trace'): markdown += f" - Contains {len(step['action_trace'])} sub-actions\n" elif step_type in ['for', 'while']: markdown += f"{i}. 🔄 **Loop** ({step_type}) - {description}\n" elif step_type == 'python': markdown += f"{i}. 🐍 **Code Execution** - {description}\n" elif step_type == 'print': markdown += f"{i}. 📝 **Message** - {description}\n" elif step_type == 'agenticSelector': markdown += f"{i}. 🎯 **Element Selection** - {description}\n" elif step_type == 'agenticMouseAction': markdown += f"{i}. 🖱️ **Mouse Action** - {description}\n" else: markdown += f"{i}. ⚡ **{step_type.title()}** - {description}\n" markdown += "\n" return markdown ``` ## GIF Recording ```python highlight={11} theme={null} import asyncio from narada import Agent, BrowserEnvironment async def main() -> None: env = BrowserEnvironment() agent = Agent(environment=env) try: # Run automation with GIF recording enabled response = await agent.run( prompt='search for "machine learning" on Google and extract the number of results', generate_gif=True # Enable GIF recording ) print("Response:", response.text) print("GIF saved to your Downloads/Narada Downloads folder!") finally: await env.close() if __name__ == "__main__": asyncio.run(main()) ``` The SDK automatically compiles frames into an animated GIF showing the complete automation trajectory. Narada automatically creates a directory structure for your GIFs: 1. **Main Directory**: `Narada Downloads` in your machine's Downloads folder 2. **Request Directory**: Directory named after your `request_id` from the [SDK response](/documentation/getting-started-with-sdk) 3. **File**: Automation saved as an animated `.gif` file If the `Narada Downloads` directory doesn't exist, it will be created automatically. Navigate to your Downloads folder and look for: ``` Downloads/ └── Narada Downloads/ └── req_abc123def456/ └── automation_recording.gif ``` Each GIF request gets its own directory named after the `request_id` for easy correlation with your SDK calls. ## Best Practices Use action traces for programmatic debugging and GIFs for visual verification Action traces provide detailed audit logs for compliance and monitoring GIFs document visual workflows while action traces capture step-by-step logic Parse action traces to analyze automation patterns and optimize workflows # Agent Maker Source: https://docs.narada.ai/documentation/agent-maker Generate custom agents from natural language goal descriptions ## What is Agent Maker? Agent Maker lets you create a custom agent by simply **describing what you want it to do** in natural language. Narada's AI generates a complete, runnable Python agent from your description, no coding or manual workflow building required. Describe a goal like "Create an agent that checks website carbon footprint and generates a report" and Agent Maker builds the entire automation for you.