# Cancel Crawl Source: https://docs.nimbleway.com/api-reference/crawl/cancel-crawl /api-reference/openapi.json delete /v2/crawl/{id} # Crawl by ID Source: https://docs.nimbleway.com/api-reference/crawl/crawl-by-id /api-reference/openapi.json get /v2/crawl/{id} # Create Crawl Source: https://docs.nimbleway.com/api-reference/crawl/create-crawl /api-reference/openapi.json post /v2/crawl # List Crawls Source: https://docs.nimbleway.com/api-reference/crawl/list-crawls /api-reference/openapi.json get /v2/crawl # Check Domain Health Source: https://docs.nimbleway.com/api-reference/domain-health/check-domain-health /api-reference/openapi.json post /v1/domain-health/check Check whether Nimble is seeing healthy traffic to one or more domains right now, sourced from real request outcomes across Search, SERP, ecommerce, and Extract traffic. # Get Driver Source: https://docs.nimbleway.com/api-reference/domain-knowledge/get-driver /api-reference/openapi.json get /v2/domain-knowledge/driver Resolves the suggested driver for a given URL or agent name. Exactly one of `url` or `agent` must be provided. # Create Extract Template Generation Source: https://docs.nimbleway.com/api-reference/extract-templates-api/create-extract-template-generation /api-reference/openapi.json post /v2/extract/templates/generations # Delete Extract Template Source: https://docs.nimbleway.com/api-reference/extract-templates-api/delete-extract-template /api-reference/openapi.json delete /v2/extract/templates/{extract_template_name} # Get Extract Template Source: https://docs.nimbleway.com/api-reference/extract-templates-api/get-extract-template /api-reference/openapi.json get /v2/extract/templates/{extract_template_name} # Get Extract Template Generation Source: https://docs.nimbleway.com/api-reference/extract-templates-api/get-extract-template-generation /api-reference/openapi.json get /v2/extract/templates/generations/{generation_id} # Get Extract Template Version Source: https://docs.nimbleway.com/api-reference/extract-templates-api/get-extract-template-version /api-reference/openapi.json get /v2/extract/templates/{extract_template_name}/versions/{version_id} # List Extract Template Versions Source: https://docs.nimbleway.com/api-reference/extract-templates-api/list-extract-template-versions /api-reference/openapi.json get /v2/extract/templates/{extract_template_name}/versions # List Extract Templates Source: https://docs.nimbleway.com/api-reference/extract-templates-api/list-extract-templates /api-reference/openapi.json get /v2/extract/templates # Run Extract Template Source: https://docs.nimbleway.com/api-reference/extract-templates-api/run-extract-template /api-reference/openapi.json post /v2/extract/templates/run Execute Extract Template Realtime Endpoint # Run Extract Template (Async) Source: https://docs.nimbleway.com/api-reference/extract-templates-api/run-extract-template-async /api-reference/openapi.json post /v2/extract/templates/async Execute Extract Template Async Endpoint # Run Extract Template (Batch) Source: https://docs.nimbleway.com/api-reference/extract-templates-api/run-extract-template-batch /api-reference/openapi.json post /v2/extract/templates/batch Execute Extract Template Batch Endpoint # Update Extract Template Source: https://docs.nimbleway.com/api-reference/extract-templates-api/update-extract-template /api-reference/openapi.json patch /v2/extract/templates/{extract_template_name} # Extract Source: https://docs.nimbleway.com/api-reference/extract/extract /api-reference/openapi.json post /v2/extract # Extract Async Source: https://docs.nimbleway.com/api-reference/extract/extract-async /api-reference/openapi.json post /v2/extract/async # Extract Batch Source: https://docs.nimbleway.com/api-reference/extract/extract-batch /api-reference/openapi.json post /v2/extract/batch # Introduction Source: https://docs.nimbleway.com/api-reference/introduction Get started with Nimble's API Nimble API turns the live web into structured data through a simple REST API. Search the web, extract any page, run pre-built Extract Templates for popular platforms or generate one for any site, run Agents for cited research, map site structures, and crawl entire websites at scale. **API v2.** This reference documents the v2 API surface. Pre-built extraction lives at `/v2/extract/templates` (**Extract Templates** — see the [Templates guide](/nimble-sdk/web-tools/extract/template)), and autonomous research at `/v2/agents` (**Agents** — see the [Agent guide](/nimble-sdk/web-search-agents/overview)). The legacy v1 endpoints keep working until they are deprecated, but new integrations should target v2. ## OpenAPI Specification The full OpenAPI (Swagger) specification is available for download. Use it to generate clients, import into tools like Postman, or feed it to coding agents. Get the complete OpenAPI 3.1 specification at [`/api-reference/openapi.json`](/api-reference/openapi.json). ## API Features Extract clean HTML and structured data from any URL with custom parsing and browser actions. Run Agents for any website — pre-built for popular platforms, or create your own. Search the web and retrieve full content from top results with optional AI summaries. Discover all URLs on a website with metadata for planning crawl strategies. Extract data from entire websites with async crawling across multiple pages. Retrieve results from async operations (extract, search, map, crawl). ## Base URL All API requests use the following base URL: ```bash theme={"system"} https://sdk.nimbleway.com/v2 ``` ## Authentication All requests require authentication using a Bearer token in the Authorization header: ```bash theme={"system"} Authorization: Bearer YOUR_API_KEY ``` Get your API key from the [Nimble Dashboard](https://online.nimbleway.com/settings/api-keys). ### Example Request ```bash theme={"system"} curl -X POST 'https://sdk.nimbleway.com/v2/extract' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --header 'Content-Type: application/json' \ --data-raw '{ "url": "https://www.example.com", "render": true }' ``` ## Response Codes Nimble uses standard HTTP status codes to indicate the success or failure of requests. | Status | Description | | - | - | | 200 | Request successful. | | 400 | Bad request - check your parameters. | | 401 | Unauthorized - invalid or missing API key. | | 402 | Payment required - insufficient budget or trial expired. | | 422 | Validation error - invalid request parameters. | | 429 | Rate limit exceeded - slow down your requests. | | 500 | Internal server error - Nimble infrastructure issue. | ## Rate Limits Nimble enforces rate limits to ensure service stability and fair usage across all users. ### Default Rate Limits | Driver Type | Rate Limit | | - | - | | `vx6`, `vx8`, `vx10` | 83 QPS (5,000 QPM) | **QPS** = Queries Per Second, **QPM** = Queries Per Minute. Rate limits apply per API key. ### Rate Limit Headers Responses include headers showing your current rate limit status: ```bash theme={"system"} ratelimit-limit: 5000 ratelimit-remaining: 4985 ``` ### Exceeding Rate Limits When you exceed the rate limit, you'll receive a 429 status code with retry information: ```json theme={"system"} { "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded. Current limit: 83 QPS", "retry_after": 2 } } ``` **Need higher limits?** Contact [sales@nimbleway.com](https://login.start-chat.com/modal/601e4da4-50a0-4dc7-8155-bb84f2952c40/27847549-dc04-4144-98a5-d40e568bf6af?magicLinkId=slFQ2j\&UID=65ab324c-ecde-4748-9f52-8e395054774b.1753267174019) for custom rate limits. ## Async Operations Several endpoints support asynchronous processing for long-running operations: * `/v2/extract/async` - Async data extraction * `/v2/extract/templates/async` - Async template extraction * `/v2/serp/async` - Async SERP retrieval * `/v2/media/async` - Async media download * `/v2/crawl` - Crawl operations (async only) ### Working with Async Tasks 1. **Start an async operation** - Returns a task ID 2. **Check task status** - Use `/v2/tasks/{id}` to check progress 3. **Retrieve results** - Get results from `/v2/tasks/{id}/results` when complete ```bash theme={"system"} # Start async extraction curl -X POST 'https://sdk.nimbleway.com/v2/extract/async' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --header 'Content-Type: application/json' \ --data-raw '{"url": "https://example.com"}' # Returns: {"task_id": "8e8cfde8-345b-42b8-b3e2-0c61eb11e00f"} # Check task status curl 'https://sdk.nimbleway.com/v2/tasks/8e8cfde8-345b-42b8-b3e2-0c61eb11e00f' \ --header 'Authorization: Bearer YOUR_API_KEY' # Get results when complete curl 'https://sdk.nimbleway.com/v2/tasks/8e8cfde8-345b-42b8-b3e2-0c61eb11e00f/results' \ --header 'Authorization: Bearer YOUR_API_KEY' ``` ## Next Steps Make your first API request in minutes Understand rate limits and technical specifications # Cancel Run Source: https://docs.nimbleway.com/api-reference/jobs/cancel-run /api-reference/openapi.json post /v2/jobs/runs/{run_id}/cancel # Create Job Source: https://docs.nimbleway.com/api-reference/jobs/create-job /api-reference/openapi.json post /v2/jobs # Delete Job Source: https://docs.nimbleway.com/api-reference/jobs/delete-job /api-reference/openapi.json delete /v2/jobs/{job_id} # Get Job Source: https://docs.nimbleway.com/api-reference/jobs/get-job /api-reference/openapi.json get /v2/jobs/{job_id} # Get Run Source: https://docs.nimbleway.com/api-reference/jobs/get-run /api-reference/openapi.json get /v2/jobs/runs/{run_id} # Get Run Artifact Source: https://docs.nimbleway.com/api-reference/jobs/get-run-artifact /api-reference/openapi.json get /v2/jobs/runs/{run_id}/artifacts/{artifact_id} # Get Run Artifact Download URL Source: https://docs.nimbleway.com/api-reference/jobs/get-run-artifact-download-url /api-reference/openapi.json get /v2/jobs/runs/{run_id}/artifacts/{artifact_id}/download-url # List Jobs Source: https://docs.nimbleway.com/api-reference/jobs/list-jobs /api-reference/openapi.json get /v2/jobs # List Run Artifacts Source: https://docs.nimbleway.com/api-reference/jobs/list-run-artifacts /api-reference/openapi.json get /v2/jobs/runs/{run_id}/artifacts # List Runs for Job Source: https://docs.nimbleway.com/api-reference/jobs/list-runs-for-job /api-reference/openapi.json get /v2/jobs/{job_id}/runs # Preview Run Artifact Source: https://docs.nimbleway.com/api-reference/jobs/preview-run-artifact /api-reference/openapi.json get /v2/jobs/runs/{run_id}/artifacts/{artifact_id}/preview # Trigger Run Source: https://docs.nimbleway.com/api-reference/jobs/trigger-run /api-reference/openapi.json post /v2/jobs/{job_id}/runs # Update Job Source: https://docs.nimbleway.com/api-reference/jobs/update-job /api-reference/openapi.json patch /v2/jobs/{job_id} # Map Source: https://docs.nimbleway.com/api-reference/map/map /api-reference/openapi.json post /v2/map # Media Download Source: https://docs.nimbleway.com/api-reference/media/media-download /api-reference/openapi.json post /v2/media Download media from a URL. Waits for the result before responding. # Media Download Async Source: https://docs.nimbleway.com/api-reference/media/media-download-async /api-reference/openapi.json post /v2/media/async Download media from a URL asynchronously. Returns a task ID immediately. # Create Agent Source: https://docs.nimbleway.com/api-reference/public-api/create-agent /api-reference/openapi.json post /v2/agents Create an Agent. Either pass `template` to materialize a pre-built template (its fields, goals, sources, and suggested questions are copied), or define the agent from scratch with `display_name`, `goals`, `sources`, and an optional `output_schema` for structured results. # Create Agent Run Source: https://docs.nimbleway.com/api-reference/public-api/create-agent-run /api-reference/openapi.json post /v2/agents/{agent_id}/runs Start an agent run. The run executes asynchronously: the response returns immediately with status `queued`, then poll `GET .../runs/{run_id}` until `completed` and fetch the output from `GET .../runs/{run_id}/result` — or set `enable_events: true` and follow `GET .../runs/{run_id}/events` for live progress. To enrich existing records instead of researching from scratch, pass them in `input_data`; this requires an `output_schema` (on the request or the agent). # Create Run Source: https://docs.nimbleway.com/api-reference/public-api/create-run /api-reference/openapi.json post /v2/agents/runs Creates a minimal persistent Agent and starts a run for it. The response includes `web_search_agent_id` for later agent and run queries. # Deactivate Agent Source: https://docs.nimbleway.com/api-reference/public-api/deactivate-agent /api-reference/openapi.json delete /v2/agents/{agent_id} Deactivate an agent. This is a soft delete: the agent can no longer start new runs, but its existing runs and their results remain retrievable. # Get Agent Source: https://docs.nimbleway.com/api-reference/public-api/get-agent /api-reference/openapi.json get /v2/agents/{agent_id} Retrieve a single Agent by ID. # Get Agent Run Source: https://docs.nimbleway.com/api-reference/public-api/get-agent-run /api-reference/openapi.json get /v2/agents/{agent_id}/runs/{run_id} Retrieve a run's current state. Poll this endpoint after creating a run: the run is finished once `status` is `completed`, `failed`, or `cancelled`. # Get Agent Run Result Source: https://docs.nimbleway.com/api-reference/public-api/get-agent-run-result /api-reference/openapi.json get /v2/agents/{agent_id}/runs/{run_id}/result Fetch the output of a completed run. The `output` is `type: "text"` (a prose answer) or `type: "json"` (structured data matching the output schema), plus `trust` metadata with per-claim citations for the answer. While the run is still `queued` or `running` this endpoint returns `409`; if the run `failed` or was `cancelled` it returns `422` with the run and error details. # Get Agent Template Source: https://docs.nimbleway.com/api-reference/public-api/get-agent-template /api-reference/openapi.json get /v2/agents/templates/{template_name} Retrieve a single agent template by its stable `template_name`. # List Agent Runs Source: https://docs.nimbleway.com/api-reference/public-api/list-agent-runs /api-reference/openapi.json get /v2/agents/{agent_id}/runs List the runs of an agent, newest first, paginated with `offset`/`limit`. # List Agent Templates Source: https://docs.nimbleway.com/api-reference/public-api/list-agent-templates /api-reference/openapi.json get /v2/agents/templates List the pre-built agent templates available to your account. Use a template's `template_name` with `POST /v2/agents` to create an agent instance from it. # List Agents Source: https://docs.nimbleway.com/api-reference/public-api/list-agents /api-reference/openapi.json get /v2/agents List the active Agents in your account. Results are scoped to the workspace resolved from your token (or the optional `workspace_id` query parameter) and paginated with `offset`/`limit`. # Stream Agent Run Events Source: https://docs.nimbleway.com/api-reference/public-api/stream-agent-run-events /api-reference/openapi.json get /v2/agents/{agent_id}/runs/{run_id}/events Stream a run's progress as [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) (`text/event-stream`). Create the run with `enable_events: true` to have events published. A keep-alive comment is sent every 15 seconds. # Update Agent Source: https://docs.nimbleway.com/api-reference/public-api/update-agent /api-reference/openapi.json patch /v2/agents/{agent_id} Update an agent with a [JSON Patch](https://datatracker.ietf.org/doc/html/rfc6902) document — an array of `{op, path, value}` operations applied to the agent, e.g. `[{"op": "replace", "path": "/display_name", "value": "My agent"}]`. Returns the updated agent. # Search Source: https://docs.nimbleway.com/api-reference/search/search /api-reference/openapi.json post /v2/search # SERP Source: https://docs.nimbleway.com/api-reference/serp/serp /api-reference/openapi.json post /v2/serp # SERP Async Source: https://docs.nimbleway.com/api-reference/serp/serp-async /api-reference/openapi.json post /v2/serp/async # SERP Batch Source: https://docs.nimbleway.com/api-reference/serp/serp-batch /api-reference/openapi.json post /v2/serp/batch # Get batch details Source: https://docs.nimbleway.com/api-reference/tasks/get-batch-details /api-reference/openapi.json get /v2/batches/{batch_id} Retrieve the details of a batch including all its tasks and completion status. # Get batch progress Source: https://docs.nimbleway.com/api-reference/tasks/get-batch-progress /api-reference/openapi.json get /v2/batches/{batch_id}/progress Retrieve lightweight progress information for a batch without fetching all task details. # List batches Source: https://docs.nimbleway.com/api-reference/tasks/list-batches /api-reference/openapi.json get /v2/batches Retrieve a paginated list of batches for the authenticated account. # List tasks Source: https://docs.nimbleway.com/api-reference/tasks/list-tasks /api-reference/openapi.json get /v2/tasks Retrieve a paginated list of tasks for the authenticated account. # Task Result Source: https://docs.nimbleway.com/api-reference/tasks/task-result /api-reference/openapi.json get /v2/tasks/{task_id}/results Retrieve the results of a completed task. # Task Status Source: https://docs.nimbleway.com/api-reference/tasks/task-status /api-reference/openapi.json get /v2/tasks/{task_id} Retrieve the details of a specific task by its ID. # Changelog Source: https://docs.nimbleway.com/changelog/release-notes Stay up to date with the latest Nimble SDK updates and improvements Track new features, improvements, and updates to the Nimble SDK and Web Tools. We ship updates regularly to enhance performance, add capabilities, and improve your developer experience. Have feedback or questions about a release? [Reach out to our team](https://portal.usepylon.com/nimble) ### Release Notes for October, 2026 ### New Features #### Extract file parsing Extract can now parse PDFs, Word documents, spreadsheets, and presentations into markdown. Add `file_parsing_options` to an Extract request and read the result from `data.markdown`. * **`mode`**: `light` for fast, text-native extraction, or `pro` for LLM-based parsing with OCR fallback on scanned and table-heavy PDFs. * **`max_pages`**: rejects PDFs over this page count before parsing. File parsing is billed per page parsed on top of the base Extract price: \$2.50 per 1,000 pages for `light` and \$7.50 per 1,000 pages for `pro` at list price. [View Documentation](/nimble-sdk/web-tools/extract/features/file-parsing) ### Release Notes for August, 2026 ### Improvements #### Web Search Agent output schema validation Custom and generated Web Search Agents now enforce hard limits on `output_schema` at the API boundary. Invalid schemas return **422** with actionable errors instead of failing at run time. Supported roots: an object with `properties`, or an array of objects. Rejected patterns include `format`, `pattern`, min/max constraints, root `anyOf`, standalone `"type": "null"`, nesting deeper than 5 levels, and more than 100 total properties. [View output schema limits](/nimble-sdk/web-search-agents/use-cases/dataset-building#output-schema-limits) ### Integrations #### Vercel eve Nimble is now an extension for [eve](https://eve.dev), Vercel's framework for durable AI agents. The [`@nimble-way/eve`](https://www.npmjs.com/package/@nimble-way/eve) package (v0.1.0) mounts Search, Extract, and Web Search Agent runs in one line, and can replace eve's built-in `web_search` and `web_fetch` so every agent turn uses Nimble. [View Documentation](/integrations/connectors/vercel-eve) ### Release Notes for July, 2026 ### New Features #### API v2 — Extract Templates & Web Search Agents Nimble's API is now **v2**. The former "agents" surface splits into two clearly separated products, each with its own endpoints and docs. * **Extract Templates** (`/v2/extract/templates/*`) — pre-built, Nimble-maintained templates that turn popular sites like Amazon, Google, and Walmart into clean, structured JSON. Pick a template, pass a few parameters, and get production-ready data at scale — no selectors, no scraping expertise. Nimble maintains each template 24/7 and auto-heals when target sites change. * **Web Search Agents** (`/v2/agents/*`) — a new autonomous research API. Ask a question and get back finished, cited research with a trust report — not just links. * **Jobs API v2** (`/v2/jobs/*`) and **Tasks & Batches v2** complete the v2 surface. ```bash theme={"system"} # Run a pre-built Extract Template curl -X POST https://sdk.nimbleway.com/v2/extract/templates/run \ -H "Authorization: Bearer YOUR-API-KEY" \ -d '{"template": "amazon_pdp", "params": {"asin": "B08N5WRWNW"}}' # Ask a Web Search Agent curl -X POST https://sdk.nimbleway.com/v2/agents/runs \ -H "Authorization: Bearer YOUR-API-KEY" \ -d '{"input": "Compare the pricing of Datadog and Grafana Cloud."}' ``` [Extract Templates](/nimble-sdk/web-tools/extract/template) · [Web Search Agents](/nimble-sdk/web-search-agents/overview) ### Breaking Change #### `/v1/agents` is now Extract Templates The legacy pre-built agents at `/v1/agents/run` have moved to Extract Templates at `/v2/extract/templates/run`. The `agent` field is now `template`. v1 endpoints keep working during a grace period — migrate to `/v2/extract/templates/*` at your own pace. See the [API Reference](/api-reference/introduction) for the full v1 → v2 mapping. "Web Search Agents" now refers only to the new research API under `/v2/agents/*`. ### Integrations #### LlamaIndex Nimble web search is now a first-class LlamaIndex tool via the [`llama-index-tools-nimble`](https://pypi.org/project/llama-index-tools-nimble/) package (v0.1.0). Call `NimbleToolSpec.search()` directly or hand it to a `FunctionAgent`, and feed the citable `Document` output into a `VectorStoreIndex` for RAG. [View Documentation](/integrations/connectors/llamaindex) #### Vercel AI SDK — Extract The `@nimble-way/ai-sdk` connector (v0.2.1) now ships an **Extract** tool (`nimbleExtract`) alongside web search — read full page content, or chain search-then-read in a single flow. [View Documentation](/integrations/connectors/vercel-ai-sdk) #### Snowflake Native App The Nimble Native App on Snowflake now covers both Search and Agents, adds App Builder Skills (including CMO Intelligence), and ships Marketplace install and setup guidance. [View Documentation](/integrations/partnerships/snowflake/native-app) ### Improvements #### Extract — automatic driver selection Extract can now pick the optimal driver for each target automatically, so you get the best success rate without manually configuring driver tiers. [View Documentation](/nimble-sdk/web-tools/extract/quickstart) #### Search moves front and center Search is now the first product under Web Tools. The legacy `include_answer` option has been removed from the Search API. [View Documentation](/nimble-sdk/web-tools/search) ### Release Notes for June, 2026 ### New Features #### Jobs API Drive Jobs programmatically, end to end. Create jobs, trigger and cancel runs, and list, preview, or download run artifacts over REST — everything the Nimble Platform UI does, now from code or CI/CD. * **Cloud I/O** — read inputs from and deliver results to your own **Amazon S3** bucket, or a **Databricks** Unity Catalog table via Delta Sharing. * **Any format** — JSON, CSV, or Parquet, in and out. [View Documentation](/nimble-sdk/agentic/jobs) ### Integrations #### Databricks A full Databricks integration suite landed this month: * **Data Enrichment** — call Nimble as SQL table functions to enrich tables directly in your warehouse. * **Genie** — query the live web in natural language through Nimble MCP, plus a **Genie Code** skill (`nimble-search`) for notebooks. * **Governance** — route Nimble through the Unity AI Gateway for centralized policy and audit. [View Documentation](/integrations/partnerships/databricks) #### More native web-search providers Nimble is now available as a native web-search provider in [OGX](/integrations/connectors/ogx) and [Omnigent](/integrations/connectors/omnigent). [View Documentation](/integrations/connectors/ogx) ### Documentation #### OpenAPI spec now public The full Nimble OpenAPI spec is now served under the API Reference, so agents and codegen tools can consume it directly. [View Documentation](/api-reference/introduction) ### Release Notes for May, 2026 ### New Features #### Domain Knowledge API Look up the recommended driver and detected antibot systems for any domain or agent before running at scale. Returns the driver tier, a list of detected antibots (e.g. `cloudflare`, `perimeterx`, `akamai`), and whether JavaScript rendering is required. ```bash theme={"system"} GET /v1/domain-knowledge/driver?url=www.lowes.com GET /v1/domain-knowledge/driver?agent=target_serp ``` [View Documentation](/nimble-sdk/web-tools/domain-knowledge) ### New Features #### Media Download API Download images, video, audio, and documents directly via the Nimble SDK. Two modes available: **Realtime** for instant binary downloads, and **Async** for large files delivered to your storage bucket. ```json theme={"system"} { "url": "https://example.com/image.jpg", "country": "US" } ``` [View Documentation](/nimble-sdk/web-tools/media) ### New Features #### Jobs Run any Web Search Agent as a managed, recurring workload — no orchestration code required. Point a job at an agent, upload your input list (CSV, Parquet, or JSON), set a schedule, and Nimble fans out one agent invocation per row on every tick. * **Any scale** — thousands to millions of inputs per run, with concurrency and retries handled automatically * **Flexible scheduling** — manual trigger, daily, weekly, monthly, or a custom cron expression * **Structured output** — results assembled into a single JSON, CSV, or Parquet file per run * **Run monitoring** — per-run success rate, completeness score, and error samples in the Nimble Platform UI Available now at [online.nimbleway.com/jobs](https://online.nimbleway.com/jobs). [View Documentation](/nimble-sdk/agentic/jobs) ### Integrations #### Azure MCP Center Nimble MCP is now an officially onboarded partner on [Azure MCP Center](https://mcp.azure.com/detail/nimble-mcp) — Microsoft's curated registry of enterprise-ready MCP servers. Install directly into VS Code with one click, authenticate via OAuth 2.1 (no API key to paste), and use Nimble's full web data toolkit in GitHub Copilot Agent Mode. * One-click install from the Azure MCP Center listing * OAuth 2.1 sign-in with PKCE — tokens managed and refreshed automatically by VS Code * Available to all Azure API Center enterprise registries automatically [View Documentation](/integrations/partnerships/microsoft/azure-mcp-center) ### New Features #### SERP API Submit search engine queries and receive structured results. Supports Google Search, Google Images, Google News, Google Maps (search, place, reviews), and Google AIO. ```json theme={"system"} { "search_engine": "google_search", "query": "nimble web data", "country": "US", "locale": "en" } ``` Async and Batch endpoints are also available — submit up to 1,000 queries in a single request using `inputs` + `shared_inputs`. [View Documentation](/nimble-sdk/web-tools/serp) ### Release Notes for April, 2026 ### New Skills #### Web Search Skills for Agent Skills New verticalized web search skills for AI coding assistants, organized into four categories: * **[Business Research](/integrations/agent-skills/web-search-skills/business-research)** -- Company Deep Dive (sourced 360° company reports), Competitor Intel (news, funding, hiring, product launches), Market Finder (discover businesses in any market or geography) * **[Marketing](/integrations/agent-skills/web-search-skills/marketing)** -- Competitor Positioning (messaging, pricing, CTAs, content gaps with before/after tracking) * **[Productivity](/integrations/agent-skills/web-search-skills/productivity)** -- Meeting Prep (attendee and company research before any meeting), Local Places (discover, enrich, and score local businesses in any neighborhood) * **[SEO](/integrations/agent-skills/web-search-skills/seo)** -- SEO Intel (keyword research, rank tracking, site audits, content gaps, competitor keywords, AI visibility, GitHub SEO) Install via the [Nimble plugin](/integrations/agent-skills/plugin-installation) for Claude Code or Cursor. [View Documentation](/integrations/agent-skills/web-search-skills/overview) ### New Features #### Agents API: Pagination Support Run agents with pagination to collect results across multiple pages. Call the agent once per page, incrementing the pagination field (commonly `page`) in `params`. ```json theme={"system"} { "agent": "amazon_serp", "params": { "keyword": "iphone 17", "page": 1 } } ``` [View Documentation](/nimble-sdk/web-tools/extract/template#pagination) ### Integrations #### Databricks Marketplace Nimble MCP Server is now available on the [Databricks Marketplace](https://marketplace.databricks.com/details/1fe5e521-e9ef-49d8-97c4-b8c0a448ce15/Nimble_Nimble-MCP-Agentic-Web-Search-Platform) as a one-click install. Creates a Unity Catalog connection that gives Databricks agents, AI Playground, and notebooks direct access to Nimble's search, extract, map, and crawl tools. [View Documentation](/integrations/partnerships/databricks/marketplace-mcp) ### Documentation #### Extract API: Advanced Options New Advanced Options page documenting low-level request controls for the Extract API. Covers `referrer_type`, `expected_status_codes`, `http2`, `session`, `tag`, `device`, and `render_options` (with nested fields for render type, timeout, iframe handling, resource blocking, and retry behavior). [View Documentation](/nimble-sdk/web-tools/extract/features/advanced-options) ### New Features #### Custom Agent Creation via API, SDK, and CLI Create custom extraction agents programmatically. Describe what data you need, provide a sample URL, and Nimble generates a production-ready agent. Iterate with natural language prompts to refine the output. Available via REST API (`POST /v1/agents/generations`), Python/Node SDKs, and CLI. [View Documentation](/nimble-sdk/web-tools/extract/template) ### New Features #### New Links Format New `links` format option for both the **Extract API** and **Agents API**. Pass `"links"` in the `formats` array to extract all URLs found on the page. Links are returned as an array of URL strings under `data.links`. ```json theme={"system"} "formats": ["html", "links"] ``` ```json theme={"system"} "data": { "links": [ "https://www.example.com/about", "https://www.example.com/contact", "https://external-site.com/resource" ] } ``` [Extract Documentation](/nimble-sdk/web-tools/extract/features/formats) | [Templates Documentation](/nimble-sdk/web-tools/extract/template) ### Release Notes for March, 2026 ### New Features #### Agents API: Batch Endpoint Submit up to **1,000 agent requests in a single request** with `POST /v1/agents/batch`. Each input runs as an independent async task. Use `shared_inputs` to set the agent and common delivery settings, and override per item in `inputs` as needed. ```json theme={"system"} { "inputs": [ { "params": { "keyword": "iphone 15" } }, { "params": { "keyword": "iphone 16" } } ], "shared_inputs": { "agent": "amazon_serp" } } ``` [View Documentation](/nimble-sdk/web-tools/extract/template#batch) ### New Features Breaking Change #### Agents API: Formats Parameter New `formats` parameter for `POST /v1/agents/run` and `POST /v1/agents/async`. Pass `"html"`, `"markdown"`, or `"headers"` to include additional data alongside `data.parsing`. ```json theme={"system"} "formats": ["html", "markdown"] ``` **Breaking change:** `data.html` is no longer returned by default. To continue receiving HTML in the response, add `"html"` to the `formats` array. **Before:** ```json theme={"system"} { "agent": "amazon_pdp", "params": { "asin": "B08N5WRWNW" } } // → data.html was included automatically ``` **After:** ```json theme={"system"} { "agent": "amazon_pdp", "params": { "asin": "B08N5WRWNW" }, "formats": ["html"] } // → data.html included only when explicitly requested ``` [View Documentation](/nimble-sdk/web-tools/extract/template) ### New Features Breaking Change #### Extract API: New Headers Format New `headers` format option. Pass `"headers"` in the `formats` array to include the HTTP response headers returned by the server. Headers are returned as a key-value object under `data.headers`. ```json theme={"system"} "formats": ["html", "headers"] ``` **Breaking change:** Headers are no longer returned by default. Previously, `data.headers` was included in every response automatically. To continue receiving headers, add `"headers"` to your `formats` array. **Before:** ```json theme={"system"} { "url": "https://example.com" } // → data.headers was included automatically ``` **After:** ```json theme={"system"} { "url": "https://example.com", "formats": ["html", "headers"] } // → data.headers included only when explicitly requested ``` [View Documentation](/nimble-sdk/web-tools/extract/features/formats) ### New Features #### Batch Extract API Submit up to **1,000 URLs in a single request** with `POST /v1/extract/batch`. Each URL runs as an independent async task. Use `shared_inputs` to set common settings (render, country, cloud delivery) across the batch, and override per item in `inputs` as needed. [View Documentation](/nimble-sdk/web-tools/extract/features/async) ### Integrations #### Integration Connectors New connector guides for plugging Nimble web tools into popular AI frameworks and platforms: * [**Smithery**](/integrations/connectors/smithery) — Connect via the Smithery MCP registry with AI SDK and TypeScript examples. * [**OpenAI**](/integrations/connectors/openai) — Use Nimble with OpenAI function calling and the Agents SDK. * [**Anthropic**](/integrations/connectors/anthropic) — Integrate with Claude tool-use API and Tool Runner. * [**Google ADK**](/integrations/connectors/google-adk) — Use Nimble with Google ADK agents via MCP. * [**LangChain**](/integrations/connectors/langchain) — LangChain tools and retrievers for Nimble web data. ### Documentation #### Nimble Docs MCP Ask questions about Nimble directly from Claude, Cursor, VS Code, or any MCP-compatible tool. The Nimble Docs MCP server exposes the full documentation knowledge base including API references, guides, and code examples, all via the `search_nimble_docs` tool. No API key required. **Server URL:** `https://docs.nimbleway.com/mcp` [View Documentation](/integrations/mcp-server/mcp-docs) ### Release Notes for February, 2026 ### New Features #### GET Agents API New endpoints for discovering and exploring available web search agents. List all available agent templates with `GET /agents` or retrieve detailed information about a specific agent including its input/output schema with `GET /agents/{agent_name}`. \ Perfect for building dynamic agent selections or understanding agent capabilities programmatically. [View Documentation](/nimble-sdk/agentic/agent-gallery) ### New Features #### Screenshot Format New `screenshot` format option for the Extract API. Capture full-page screenshots as base64-encoded PNG images for visual verification, monitoring, and archival. Screenshots automatically enable JavaScript rendering (VX8/VX10 driver). [View Documentation](/nimble-sdk/web-tools/extract/features/formats) ### Documentation #### Documentation Updates * **Callbacks & Delivery Guide**: Comprehensive guide covering all async result delivery options - polling, webhooks, and cloud delivery to S3/GCS. Includes setup instructions for bucket permissions. [View Guide](/nimble-sdk/admin/callbacks-and-delivery) * **Integration Documentation**: New guide covering all available integration and AI connectors to Nimble SDK capabilities. [View Integrations](/integrations) ### New Features #### Agent Skills API Advanced web search skills powered by Nimble Search API. Built on the open-source [Agent Skills](https://agentskills.io/) standard for cross-platform agent compatibility, connected to you Claude, Cursor and more. Features 8 specialized focus modes (general, coding, news, academic, shopping, social, geo, location) with AI-powered answer generation and smart result filtering. [Try Now](/integrations/agent-skills/web-tools-skills/nimble-web-expert) ### Release Notes for January, 2026 ### Documentation #### Comprehensive Guides * **Quickstart Pages**: New quickstart guides for all Web Tools with installation steps, basic usage examples, common patterns, and next steps. Get started in under 5 minutes. * **Usage Documentation**: Detailed usage pages for each Web Tool covering all parameters, response formats, best practices, common use cases, and limitations. * **API Reference**: Complete API reference with request/response schemas, error codes, rate limits, and authentication methods for all endpoints. [Try Now](/api-reference/introduction) * **Account Management**: New guide covering API key generation, team member management, permissions, and security best practices. [View Guide](/nimble-sdk/admin/account-management) * **Integration Documentation**: New guide covering all available integration and AI connectors to Nimble SDK capabilities. [View Integrations](/integrations) ### New Features #### Extract API The `/extract` retrieves clean HTML, text, and structured data from any webpage. Supports JavaScript rendering, stealth mode for anti-bot protection, custom parsing with CSS selectors, browser actions, screenshots, and network request capture. [Try Now](/nimble-sdk/web-tools/extract/quickstart) ### New Features #### Web Search Agents API The `/agent` are ready-to-use web search agent extractors for popular websites like Amazon, Google, LinkedIn, and hundreds more. No coding or CSS selectors required - just provide the template name, and template inputs such as search term or product ID and get structured data instantly. Agents are maintained 24/7 by Nimble and automatically updated when sites change. [Try Now](/nimble-sdk/web-tools/extract/template) ### New Features #### Search API The `/search` performs web searches and retrieves parsed content from top results. Supports three search depth modes (`lite`, `fast`, `deep`) for flexible cost/quality tradeoffs, and AI-powered answer summaries from search results. [Try Now](/nimble-sdk/web-tools/search) ### New Features #### Map API The `/map` perform fast URL discovery and site structure mapping. Ideal for discovering all pages on a website, extracting sitemap URLs, and understanding site architecture without deep crawling overhead. Supports subdomain filtering and exact domain matching. [Try Now](/nimble-sdk/web-tools/map) ### New Features #### Crawl API The `/crawl` perform deep website crawling for comprehensive data extraction across multiple pages. Features smart navigation, data normalization, and multi-page scraping with automatic traversing handling. [Try Now](/nimble-sdk/web-tools/crawl) # Build Your First Extract Template Source: https://docs.nimbleway.com/guides/build-first-agent-tutorial Give Claude one prompt and let the Nimble plugin handle Extract Template discovery, custom template generation, and script writing, end to end ## What You'll Build A Python script that pulls running shoe data from **Amazon**, **Walmart**, and **Nike.com** and outputs a ranked, cross-retailer price comparison — all from a single prompt to your AI coding assistant. This tutorial shows the core power of Nimble: **Extract Templates work with any website**, not just the ones in the gallery. The Nimble plugin handles the rest: * **Finds** pre-built Extract Templates for sites already in the gallery (Amazon, Walmart) * **Generates** a custom template on the fly for Nike.com, a site with no pre-built template * **Writes** the full analysis script using the templates it collected ## Prerequisites Sign up free and grab your API key from Settings → API Keys Install the Nimble plugin for Claude Code or Cursor You'll also need Python 3.8+ and the Nimble SDK: ```bash theme={"system"} pip install nimble_python ``` *** ## Step 1: Give Claude the Prompt Open a new session in Claude Code (or Cursor) and paste this prompt: ``` Build a Python script that runs competitive analysis on running shoes across Amazon, Walmart, and Nike.com. Use Nimble for all data collection. For each retailer: - Search the Nimble Extract Template gallery for an existing template. Use it if one exists. - If no template exists for that site, use Nimble to generate a custom Extract Template, then publish it for reuse. The script should collect product name, price, rating (if available), and URL from each source, then print a unified list sorted by price and save the full results to shoe_analysis.json. My Nimble API key is set as the NIMBLE_API_KEY environment variable. ``` ``` Build a Python script that runs competitive analysis on running shoes across Amazon, Walmart, and Nike.com. Use Nimble for all data collection. For each retailer: - Search the Nimble Extract Template gallery for an existing template. Use it if one exists. - If no template exists for that site, use Nimble to generate a custom Extract Template, then publish it for reuse. The script should collect product name, price, rating (if available), and URL from each source, then print a unified list sorted by price and save the full results to shoe_analysis.json. My Nimble API key is set as the NIMBLE_API_KEY environment variable. ``` That's it. Claude takes it from here. *** ## What Claude Does Next Claude works through the task autonomously using the Nimble plugin. Here's what happens under the hood: Claude calls `nimble_extract_templates_list` with the query `"amazon"` and finds `amazon_serp` — a pre-built Extract Template for Amazon search results. It inspects the schema and confirms `keyword` is the required input. Same flow for Walmart — finds `walmart_search`, confirms it takes a `keyword` param and returns product name, price, and rating. No public Extract Template exists for `nike.com`. Claude calls `nimble_extract_templates_generate` with a description of what's needed, waits for the template to be created, runs a test extraction, and publishes it as `nike_running_shoes_plp`. With all three templates confirmed, Claude writes `shoe_analysis.py` using the Nimble Python SDK and the template names it just collected. The script Claude produces will look something like this: ```python theme={"system"} import json import os from nimble_python import Nimble nimble = Nimble(api_key=os.environ["NIMBLE_API_KEY"]) KEYWORD = "running shoes" NIKE_URL = "https://www.nike.com/w/mens-running-shoes" def fetch_amazon(): result = nimble.extract.templates.run( template="amazon_serp", params={"keyword": KEYWORD} ) products = result.to_dict()["data"]["parsing"] return [ { "source": "Amazon", "name": p.get("product_name"), "price": p.get("price"), "rating": p.get("average_rating"), "url": p.get("product_url"), } for p in products if p.get("price") ] def fetch_walmart(): result = nimble.extract.templates.run( template="walmart_search", params={"keyword": KEYWORD} ) products = result.to_dict()["data"]["parsing"] return [ { "source": "Walmart", "name": p.get("product_name"), "price": p.get("price"), "rating": p.get("rating"), "url": p.get("product_url"), } for p in products if p.get("price") ] def fetch_nike(): result = nimble.extract.templates.run( template="nike_running_shoes_plp", params={"url": NIKE_URL} ) products = result.to_dict()["data"]["parsing"] return [ { "source": "Nike", "name": p.get("product_name"), "price": p.get("price"), "colors": p.get("colors", []), "url": p.get("url"), } for p in products if p.get("price") ] def run_analysis(): print(f'Competitive analysis: "{KEYWORD}"\n') all_products = [] print("Fetching Amazon...") all_products.extend(fetch_amazon()) print("Fetching Walmart...") all_products.extend(fetch_walmart()) print("Fetching Nike...") all_products.extend(fetch_nike()) sorted_products = sorted(all_products, key=lambda x: x["price"]) print(f"\n{'=' * 55}") print(f" RESULTS — {len(all_products)} products across 3 retailers") print(f"{'=' * 55}\n") for i, p in enumerate(sorted_products[:15], 1): stars = f" ★{p['rating']}" if p.get("rating") else "" colors = f" ({len(p['colors'])} colors)" if p.get("colors") else "" print(f"{i:2}. [{p['source']:7}] ${p['price']:<8.2f} {p['name']}{stars}{colors}") with open("shoe_analysis.json", "w") as f: json.dump(all_products, f, indent=2) print(f"\nFull results saved to shoe_analysis.json") if __name__ == "__main__": run_analysis() ``` The exact template names (e.g. `nike_running_shoes_plp`) are chosen by the skill at generation time. Claude will use whatever names were returned and write the script accordingly — you don't need to track them manually. *** ## Step 2: Run the Script ```bash theme={"system"} python shoe_analysis.py ``` Expected output: ``` Competitive analysis: "running shoes" Fetching Amazon... Fetching Walmart... Fetching Nike... ======================================================= RESULTS — 62 products across 3 retailers ======================================================= 1. [Walmart ] $19.98 Athletic Works Men's Knit Running Shoe ★4.1 2. [Walmart ] $29.98 Starter Men's Athletic Running Shoe ★4.0 3. [Walmart ] $44.97 Skechers Go Run Consistent ★4.3 4. [Amazon ] $54.99 New Balance Men's 515 V3 Sneaker ★4.5 5. [Walmart ] $54.98 Nike Downshifter 12 ★4.5 6. [Amazon ] $64.99 Adidas Men's Runfalcon 3.0 ★4.6 7. [Amazon ] $69.99 Nike Men's Revolution 7 ★4.6 8. [Nike ] $110.00 Nike Pegasus Trail 5 (3 colors) 9. [Amazon ] $119.95 Brooks Ghost 15 ★4.7 10. [Nike ] $130.00 Nike Pegasus 41 (5 colors) ... Full results saved to shoe_analysis.json ``` *** ## What's Next Need help choosing the right approach or scaling up? View plans and subscribe # Cookbooks Source: https://docs.nimbleway.com/guides/cookbooks # Nimble Documentation Source: https://docs.nimbleway.com/home Create Agents for any website. Extract structured, real-time knowledge for AI applications, data pipelines, and analytics — no scraping expertise required.
# Build with Nimble

Agents that turn any website into structured knowledge for AI

Ask Nimble → Install Plugin → Quickstart Guide → API Reference →
## See Nimble in Action
Scalable data collection with stealth unblocking --> Get clean, real-time HTML and structured data from any URL
Input ```python Python theme={"system"} from nimble_python import Nimble nimble = Nimble(api_key="YOUR-API-KEY") result = nimble.extract.run( url="https://www.nimbleway.com", formats=["html", "markdown"] ) ``` ```typescript TypeScript theme={"system"} import Nimble from "@nimble-way/nimble-js"; const nimble = new Nimble({ apiKey: "YOUR-API-KEY" }); const result = await nimble.extract.run({ url: "https://www.nimbleway.com", formats: ["html", "markdown"], }); ``` ```go Go theme={"system"} package main import ( "context" nimble "github.com/Nimbleway/nimble-go" "github.com/Nimbleway/nimble-go/option" ) func main() { client := nimble.NewClient(option.WithAPIKey("YOUR-API-KEY")) result, err := client.Extract.Run(context.Background(), nimble.ExtractRunParams{ URL: "https://www.nimbleway.com", Formats: []string{"html", "markdown"}, }) _, _ = result, err } ``` ```bash CLI theme={"system"} nimble extract run \ --url "https://www.nimbleway.com" \ --format html --format markdown ``` ```bash cURL theme={"system"} curl -X POST 'https://sdk.nimbleway.com/v2/extract' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data-raw '{ "url": "https://www.nimbleway.com", "formats": ["html", "markdown"] }' ```
Output ```json theme={"system"} { "url": "https://www.nimbleway.com", "status": "success", "data": { "html": "...", "markdown": "# Nimble SDK\n...", "parsing": {...}, "screenshots": [...], "network_capture": [...], ... and more }, "metadata": { "driver": "vx8", ... and more } } ```
Accurate, real-time web search with --> AI Agents search the live web to retrieve precise information
Input ```python Python theme={"system"} from nimble_python import Nimble nimble = Nimble(api_key="YOUR-API-KEY") result = nimble.search( query="latest AI developments", focus="general", max_results=5, search_depth="standard" ) ``` ```typescript TypeScript theme={"system"} import Nimble from "@nimble-way/nimble-js"; const nimble = new Nimble({ apiKey: "YOUR-API-KEY" }); const result = await nimble.search({ query: "latest AI developments", focus: "general", max_results: 5, search_depth: "standard", }); ``` ```go Go theme={"system"} package main import ( "context" nimble "github.com/Nimbleway/nimble-go" "github.com/Nimbleway/nimble-go/option" "github.com/Nimbleway/nimble-go/packages/param" ) func main() { client := nimble.NewClient(option.WithAPIKey("YOUR-API-KEY")) result, err := client.Search(context.Background(), nimble.SearchParams{ Query: "latest AI developments", MaxResults: param.NewOpt(int64(5)), SearchDepth: nimble.SearchParamsSearchDepthStandard, }) _, _ = result, err } ``` ```bash CLI theme={"system"} nimble search \ --query "latest AI developments" \ --focus general \ --max-results 5 \ --search-depth standard ``` ```bash cURL theme={"system"} curl -X POST 'https://sdk.nimbleway.com/v2/search' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data-raw '{ "query": "latest AI developments", "focus": "general", "max_results": 5, "search_depth": "standard" }' ```
Output ```json theme={"system"} { "request_id": "84f08ac1-bb5f-4b6f-8447-2d21cb930416", "total_results": 5, "results": [ { "title": "Latest AI Developments", "url": "https://example.com/ai", "description": "Recent breakthroughs..." }, ... and more ] } ```
Fast URL discovery and site structure mapping --> Easily plan extraction workflows
Input ```python Python theme={"system"} from nimble_python import Nimble nimble = Nimble(api_key="YOUR-API-KEY") result = nimble.map( url="https://www.nimbleway.com", sitemap="include" ) ``` ```typescript TypeScript theme={"system"} import Nimble from "@nimble-way/nimble-js"; const nimble = new Nimble({ apiKey: "YOUR-API-KEY" }); const result = await nimble.map({ url: "https://www.nimbleway.com", sitemap: "include", }); ``` ```go Go theme={"system"} package main import ( "context" nimble "github.com/Nimbleway/nimble-go" "github.com/Nimbleway/nimble-go/option" ) func main() { client := nimble.NewClient(option.WithAPIKey("YOUR-API-KEY")) result, err := client.Map(context.Background(), nimble.MapParams{ URL: "https://www.nimbleway.com", Sitemap: nimble.MapParamsSitemapInclude, }) _, _ = result, err } ``` ```bash CLI theme={"system"} nimble map \ --url "https://www.nimbleway.com" \ --sitemap include ``` ```bash cURL theme={"system"} curl -X POST 'https://sdk.nimbleway.com/v2/map' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data-raw '{ "url": "https://www.nimbleway.com", "sitemap": "include" }' ```
Output ```json theme={"system"} { "success": true, "links": [ { "url": "https://www.nimbleway.com", "title": "Nimble - Web Data Platform", "description": "AI-powered web data collection and extraction platform" }, { "url": "https://www.nimbleway.com/pricing", "title": "Pricing", "description": "Nimble pricing plans and features" } ... and more ] } ```
Extract contents from entire websites in a single request --> Collect large volumes of web data automatically
Input ```python Python theme={"system"} from nimble_python import Nimble nimble = Nimble(api_key="YOUR-API-KEY") result = nimble.crawl.run( url="https://docs.nimbleway.com/nimble-sdk", limit=4 ) ``` ```typescript TypeScript theme={"system"} import Nimble from "@nimble-way/nimble-js"; const nimble = new Nimble({ apiKey: "YOUR-API-KEY" }); const result = await nimble.crawl.run({ url: "https://docs.nimbleway.com/nimble-sdk", limit: 4, }); ``` ```go Go theme={"system"} package main import ( "context" nimble "github.com/Nimbleway/nimble-go" "github.com/Nimbleway/nimble-go/option" "github.com/Nimbleway/nimble-go/packages/param" ) func main() { client := nimble.NewClient(option.WithAPIKey("YOUR-API-KEY")) result, err := client.Crawl.Run(context.Background(), nimble.CrawlRunParams{ URL: "https://docs.nimbleway.com/nimble-sdk", Limit: param.NewOpt(int64(4)), }) _, _ = result, err } ``` ```bash CLI theme={"system"} nimble crawl run \ --url "https://docs.nimbleway.com/nimble-sdk" \ --limit 4 ``` ```bash cURL theme={"system"} curl -X POST 'https://sdk.nimbleway.com/v2/crawl' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data-raw '{ "url": "https://docs.nimbleway.com/nimble-sdk", "limit": 4 }' ```
Output ```json theme={"system"} { "crawl_id": "85b21e98-69c5-4aa5-9d25-261a55bbf0db", "url": "https://docs.nimbleway.com/nimble-sdk", "status": "queued", "crawl_options": { "sitemap": "include", "limit": 4, "max_discovery_depth": 5, ... and more } } ```
Agents research the live web and answer with receipts --> Ask like you'd brief an analyst, get a cited answer with a trust report
Input ```python Python theme={"system"} from nimble_python import Nimble nimble = Nimble(api_key="YOUR-API-KEY") run = nimble.agents.run( input="Compare the pricing of Datadog, Grafana Cloud, and New Relic.", ) ``` ```typescript TypeScript theme={"system"} import Nimble from "@nimble-way/nimble-js"; const nimble = new Nimble({ apiKey: "YOUR-API-KEY" }); const run = await nimble.agents.run({ input: "Compare the pricing of Datadog, Grafana Cloud, and New Relic.", }); ``` ```go Go theme={"system"} package main import ( "context" nimble "github.com/Nimbleway/nimble-go" "github.com/Nimbleway/nimble-go/option" ) func main() { client := nimble.NewClient(option.WithAPIKey("YOUR-API-KEY")) run, err := client.Agents.Run(context.Background(), nimble.AgentRunParams{ Input: "Compare the pricing of Datadog, Grafana Cloud, and New Relic.", }) _, _ = run, err } ``` ```bash CLI theme={"system"} nimble agents run \ --input "Compare the pricing of Datadog, Grafana Cloud, and New Relic." ``` ```bash cURL theme={"system"} curl -X POST 'https://sdk.nimbleway.com/v2/agents/runs' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data-raw '{ "input": "Compare the pricing of Datadog, Grafana Cloud, and New Relic." }' ```
Output ```json theme={"system"} { "output": { "type": "text", "content": "Datadog anchors the premium end of the market, with Pro pricing from $15 per host [1]. Grafana Cloud undercuts it with a free tier [2]...", "trust": { "confidence": "high", "claims": [ { "id": 1, "confidence": "high", "reasoning": "Backed by a primary source (official)", "citations": [ { "url": "https://datadoghq.com/pricing", ... } ] }, ... and more ] } } } ```
Pre-built Extract Templates for popular sites --> Structured data that never breaks, or generate a template for any website
Input ```python Python theme={"system"} from nimble_python import Nimble nimble = Nimble(api_key="YOUR-API-KEY") result = nimble.extract.templates.run( template="amazon_pdp", params={ "asin": "B0DKB1GWML" } ) ``` ```typescript TypeScript theme={"system"} import Nimble from "@nimble-way/nimble-js"; const nimble = new Nimble({ apiKey: "YOUR-API-KEY" }); const result = await nimble.extract.templates.run({ template: "amazon_pdp", params: { asin: "B0DKB1GWML", }, }); ``` ```go Go theme={"system"} package main import ( "context" nimble "github.com/Nimbleway/nimble-go" "github.com/Nimbleway/nimble-go/option" ) func main() { client := nimble.NewClient(option.WithAPIKey("YOUR-API-KEY")) result, err := client.Extract.Templates.Run(context.Background(), nimble.ExtractTemplateRunParams{ Template: "amazon_pdp", Params: map[string]any{ "asin": "B0DKB1GWML", }, }) _, _ = result, err } ``` ```bash CLI theme={"system"} nimble extract:templates run \ --template amazon_pdp \ --params '{asin: B0DKB1GWML}' ``` ```bash cURL theme={"system"} curl -X POST 'https://sdk.nimbleway.com/v2/extract/templates/run' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data-raw '{ "template": "amazon_pdp", "params": { "asin": "B0DKB1GWML" } }' ```
Output ```json theme={"system"} { "url": "https://www.amazon.com/dp/B0DKB1GWML", "data": { "html": "...", "parsing": { "product_title": "Apple iPhone 16 Pro Max...", "product_description": ".....", "brand": "Apple", "web_price": 819.97, "availability": true, ... and more } }, "metadata":{ "driver": "vx8", ... and more } } ```
Route requests through premium residential IPs --> Scale fast with without getting blocked
Input ```bash theme={"system"} # Proxy connection string PROXY=http://account-ACCOUNT-pipeline-PIPELINE-country-US:PASSWORD@ip.nimbleway.com:7000 # Request through US residential IP curl -x $PROXY https://ipinfo.io/json ```
Output ```json theme={"system"} { "ip": "123.0.0.123", "city": "Los Angeles", "region": "California", "country": "US", "loc": "34.0522,-118.2437", "org": "AS12345 Example ISP", "timezone": "America/Los_Angeles" } ```
Nimble
# Plugin Installation Source: https://docs.nimbleway.com/integrations/agent-skills/plugin-installation Install the Nimble plugin and turn your AI coding assistant into a web data expert Install the Nimble plugin and turn Claude Code or Cursor into a web data powerhouse. Search, extract, scrape, and build reusable Extract Templates, all from your AI coding assistant. The plugin bundles the Nimble skills and an MCP server connection into a single package. ## What's Included | Component | Description | | - | - | | **nimble-web-expert** skill | Get live web data instantly. Search, extract, map, and crawl via Nimble CLI. Also runs Extract Templates and Agents | | **MCP server config** | Pre-configured connection to the Nimble MCP server | ## Skills Overview **Get web data immediately.** Fast, one-off extractions. Scrape pages, fetch URLs, search the web, crawl site sections. This is the only way your AI coding assistant can access live websites. **Triggers:** Fetching any URL, scraping data, web search, bulk crawling, running Extract Templates and Agents ## Prerequisites 1. **Nimble CLI**: Install globally: ```bash theme={"system"} npm i -g @nimble-way/nimble-cli ``` 2. **Nimble API Key**: [Sign up](https://online.nimbleway.com/signup) and generate a key from your [Settings → API Keys](/nimble-sdk/admin/account-management#api-keys) 3. Set the environment variable: ```bash theme={"system"} export NIMBLE_API_KEY="your-api-key-here" ``` Or add to `~/.claude/settings.json` (Claude Code): ```json theme={"system"} { "env": { "NIMBLE_API_KEY": "your-api-key-here" } } ``` ## Install by Platform Installs the Nimble skills and configures the MCP server. ```bash theme={"system"} claude plugin marketplace add Nimbleway/agent-skills && \ claude plugin install nimble@nimble-plugin-marketplace ``` Run `/mcp` and confirm `nimble` appears in your connected servers. ### Alternative: local plugin directory Clone the repo and load it as a local plugin: ```bash theme={"system"} git clone https://github.com/Nimbleway/agent-skills.git claude --plugin-dir /path/to/agent-skills ``` One click, from the Anthropic and Partners directory inside Claude. Install in Claude Cowork Approve the scopes in your browser. There is no API key to paste or manage. ### Step-by-step, with screenshots In a new Cowork task, click the **+** button, hover **Plugins**, then click **Manage plugins**. Cowork new-task input with the + menu open showing Plugins > Manage plugins Alternatively, from the **Customize** screen, click **+** next to **Personal plugins** and choose **Browse plugins**. Customize > Personal plugins with the Browse plugins / Create plugin menu open Switch to the **Plugins** tab, select **Anthropic & Partners**, and search for `Nimble`. Directory with Plugins tab and Anthropic & Partners filter, search 'Nimble' showing the Nimble plugin card Open the Nimble plugin and click **Install**. The plugin's skills, agents, and MCP connector register automatically. Nimble plugin detail page showing 14 skills, 2 agents, and 1 MCP connector Nimble now appears under **Personal plugins** in Customize, with **Skills**, **Connectors**, and **Agents** sub-sections. Customize sidebar showing the installed Nimble plugin with Skills, Connectors, and Agents sub-items Open **Connectors**, then click **Connect** to start OAuth. Nimble plugin Connectors page with the nimble (CUSTOM) connector and a Connect button Review the permissions Claude is requesting and click **Approve** to authenticate with your Nimble workspace. OAuth approval dialog: Claude is requesting access to Nimble with permissions to verify identity, search the web, extract data, manage Web Search Agents, and maintain access offline Once approved, the Nimble skills, agents, and MCP tools are live across your Cowork conversations. For more on connecting Nimble to other Claude surfaces, see the [Claude AI Connectors guide](/integrations/connectors/anthropic/claude-connectors). One click, then confirm the install in Cursor. Add to Cursor Then replace `NIMBLE_API_KEY` in **Settings → MCP Servers → nimble-mcp-server** with your [Nimble API key](https://online.nimbleway.com/settings/api-keys). The deeplink ships a placeholder, not a key. Teaches the agent which Nimble product fits a given job. ```bash theme={"system"} npx skills add Nimbleway/agent-skills -a cursor ``` Open Settings, then MCP, and confirm `nimble-mcp-server` is listed. Add to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global): ```json theme={"system"} { "mcpServers": { "nimble-mcp-server": { "url": "https://mcp.nimbleway.com/mcp", "headers": { "Authorization": "Bearer NIMBLE_API_KEY" } } } } ``` Restart Cursor for changes to take effect. The `npx skills add` step above also copies the Cursor rules into your project. Clone the repo and open the `agent-skills` folder in Cursor: ```bash theme={"system"} git clone https://github.com/Nimbleway/agent-skills.git ``` Cursor auto-discovers: * `skills/` the Nimble skills * `rules/` Cursor rules (auto-loaded) * `mcp.json` MCP server connection Click **Plugins** in the Codex left menu. Codex left menu listing New chat, Pull requests, Sites, Scheduled, and Plugins Search for `Nimble` in **Plugins** and open it. Click **Install plugin**. This installs the Nimble skills and the hosted MCP connection together. Nimble plugin page in Codex showing the Install plugin button, three starter prompts, the plugin description, and one MCP server named Nimble **Codex is requesting access** opens in your browser. Click **Approve**, then authenticate with your Nimble workspace. There is no API key to paste or manage. Codex asks to search the web and extract data from websites, create and manage Agents, maintain access when offline, and verify identity. OAuth dialog titled Codex is requesting access, listing permissions to search the web and extract data, create and manage web search agents, maintain access when offline, and verify identity, with Cancel and Approve buttons Bundled skills register on a new session. Start a new task, type `@nimble`, and select **Nimble**. A Nimble chip appears on the prompt, and everything you send in that task can reach the live web. Codex composer with a Nimble chip attached to the prompt Full 2026 report on best AI Frameworks for building agents Adding the Nimble repository as a marketplace source installs the same packaged plugin. Replace the **Find Nimble** step with these two, then continue from **Install Nimble**. 1. In **Plugins**, click **Add** at the top right, then **Add a marketplace**. Codex Plugins screen with the Add menu open, offering Create plugin, Add a marketplace, and Record a skill 2. In **Add plugin marketplace**, paste this into **Source**, then click **Add marketplace**: ```text theme={"system"} https://github.com/Nimbleway/agent-skills.git ``` Leave **Git ref** and **Sparse paths** empty. The repository is public, so no GitHub credentials and no local clone are needed. Add plugin marketplace dialog with the Nimble agent-skills repository in the Source field, empty Git ref and Sparse paths, and an Add marketplace button Codex stores MCP configuration in `~/.codex/config.toml`, or a project-scoped `.codex/config.toml`. Add: ```toml theme={"system"} [mcp_servers.nimble-mcp-server] url = "https://mcp.nimbleway.com/mcp" bearer_token_env_var = "NIMBLE_API_KEY" ``` Codex reads `NIMBLE_API_KEY` from your environment at runtime, so the key is never written to the config file. Installs the Nimble skills into Codex's skill discovery paths (`~/.agents/skills/`). ```bash theme={"system"} npx skills add Nimbleway/agent-skills -a codex ``` Run `/mcp` inside the Codex TUI to confirm the server, and `/skills` to confirm the skills. Restart Codex if they do not appear. ### Verify with an Agent Works the same in Desktop and CLI. A `/mcp` check proves the connection. To prove Nimble actually reaches the live web, run a research prompt that has to cite its sources: ```text theme={"system"} Full 2026 report on best AI Frameworks for building agents. Include the source URL for every claim. ``` In Desktop, attach **Nimble** with `@` first so Codex routes the prompt to the plugin. A correct result is an [Agent](/nimble-sdk/web-search-agents/overview) run: Nimble reads current pages and returns findings where every claim carries the URL it came from. If the answer arrives with no URLs, the model answered from memory and Nimble is not being used. ### Troubleshooting | Symptom | Where | Cause | Action | | - | - | - | - | | The marketplace source is rejected | Desktop | A typo in the URL, or no network route to github.com | Confirm the repo is reachable with `git ls-remote https://github.com/Nimbleway/agent-skills.git` | | `@nimble` returns no matches | Desktop | Bundled skills register only on a new session | Start a new task, or restart Codex, then type `@nimble` again | | The OAuth window never opens, or never returns | Desktop | A blocked popup, or no route to `mcp.nimbleway.com` | Allow popups for Codex and retry. Check connectivity with `curl -I https://mcp.nimbleway.com/mcp` | | `/mcp` shows Nimble as not connected | Both | OAuth was cancelled or expired, or `NIMBLE_API_KEY` is unset in the CLI | Desktop: reconnect from the plugin's MCP settings and approve the scopes again. CLI: `echo $NIMBLE_API_KEY`, export it, then restart Codex | | `/skills` lists nothing after `npx skills add` | CLI | The skills landed outside Codex's discovery path | Confirm the files exist under `~/.agents/skills/` and re-run with `-a codex` | The packaged plugin is built from the [Nimble agent-skills repository](https://github.com/Nimbleway/agent-skills). [Cortex Code](https://docs.snowflake.com/en/user-guide/cortex-code/cortex-code), Snowflake's native AI coding agent for the terminal, installs the Nimble MCP server with a single command. OAuth handles authentication — no API key required. Adds the Nimble MCP server to Snowflake's terminal agent. ```bash theme={"system"} cortex mcp add nimble https://mcp.nimbleway.com/mcp --transport http ``` No `Authorization` header is needed. The Nimble MCP server advertises OAuth and Cortex Code runs the flow on first connection, storing tokens in your OS keychain. Run `cortex mcp list` to confirm, then `cortex mcp start`. Cortex Code writes the server to `~/.snowflake/cortex/mcp.json`. Once `nimble` shows as connected, its tools are namespaced as `mcp__nimble__search`, `mcp__nimble__extract`, `mcp__nimble__map`, and `mcp__nimble__crawl`. For organization-wide rollout via managed settings, see the [full Cortex Code integration guide](/integrations/partnerships/snowflake/cortex-code). Installs the Nimble skills into your project. ```bash theme={"system"} npx skills add Nimbleway/agent-skills ``` Run `npx skills add Nimbleway/agent-skills --list` to see what was installed. The skills reach Agents and Extract Templates through the MCP server. After installing via `npx skills`, connect the server manually: ```bash theme={"system"} claude mcp add --transport http nimble-mcp-server https://mcp.nimbleway.com/mcp \ --header "Authorization: Bearer ${NIMBLE_API_KEY}" ``` ## What's Next End-to-end tutorial: competitive price analysis across Amazon, Walmart, and Nike, from a single prompt Prefer a visual interface? Create agents in the browser Need enterprise scale or managed delivery? View the full plugin source on GitHub # Business Research Source: https://docs.nimbleway.com/integrations/agent-skills/web-search-skills/business-research 360° company research, competitor monitoring, and market discovery Research companies, monitor competitors, and discover businesses in any market. These skills run parallel web searches, synthesize findings into structured reports, and track changes across runs. For shared architecture, install instructions, and the Private Local Web Index, see the [Web Search Skills overview](/integrations/agent-skills/web-search-skills/overview). ## Company Deep Dive Source code for this skill A sourced 360-degree report on any company covering funding, leadership, product/tech, market position, news, and strategic outlook. **Example prompts:** ``` "tell me about Stripe" "due diligence on Figma" "background on Cloudflare before our partnership call" ``` **How it works:** Confirms the company identity via web search, then runs parallel research across five dimensions (funding, product, leadership, news, market position). Extracts full content from the top 5-8 most informative URLs and delivers a structured report. **Output includes:** * Quick assessment (3-5 key takeaways) * Per-dimension sections with dates and source URLs * Strategic outlook * Full source list **Modes:** Full deep dive (default), quick mode (skips deep extraction), refresh mode (focuses on what changed). *** ## Competitor Intel Source code for this skill Monitors competitors via live web searches and produces a prioritized intelligence briefing. Compares against previous findings to highlight only what's new. **Example prompts:** ``` "what are my competitors doing" "competitor update" "battlecard for Stripe" ``` **How it works:** Loads known competitors from the business profile (or helps identify them on first run). Researches each competitor in parallel for news, funding, hiring signals, product launches, and social activity. Validates high-priority signals and compares against prior briefings. **Output includes:** * TL;DR with top 3-5 signals across all competitors * Per-competitor section with dated, sourced signals * Cross-competitor patterns and recommended actions **Signal priority:** | Priority | Examples | | - | - | | **P1 (High)** | M\&A, leadership changes, major funding (\$10M+) | | **P2 (Medium)** | Product launches, partnerships, major hires | | **P3 (Low)** | Blog posts, minor hires, opinion pieces | *** ## Market Finder Source code for this skill Discovers all businesses of a given type in any geography. Two modes: **Discovery** finds businesses from scratch, **Audit** compares an existing list against fresh discovery to find gaps. **Example prompts:** ``` "find all dentists in Austin, TX" "build a list of SaaS companies in the CRM space" "audit my list of clinics against what's actually out there" ``` **How it works:** Auto-detects the business vertical and loads optimized search strategies. For large areas, tiles the geography into metro areas. Runs parallel searches across Google Maps, Yelp, BBB, and vertical-specific directories. Deduplicates with three-tier matching (place ID, domain, fuzzy name + city). **Output includes:** * Ranked business list with name, address, phone, website, rating, review count, and strength score * Source links per business * In Audit mode: matched, discovered-only, and reference-only categories with gap analysis **Vertical presets:** Healthcare, SaaS/Software, Restaurants, Legal/Financial, Auto/Home Services, and a general fallback. # Marketing Source: https://docs.nimbleway.com/integrations/agent-skills/web-search-skills/marketing Track competitor positioning, messaging shifts, pricing changes, and content gaps Analyze how competitors position themselves online. These skills scrape competitor websites, extract messaging and value props, and track positioning shifts over time. For shared architecture, install instructions, and the Private Local Web Index, see the [Web Search Skills overview](/integrations/agent-skills/web-search-skills/overview). ## Competitor Positioning Source code for this skill Tracks how competitors position themselves online. Scrapes homepages, features pages, pricing pages, and blogs to extract messaging, value props, CTAs, and pricing models. Compares against previous snapshots to surface positioning shifts with before/after tracking. **Example prompts:** ``` "competitor positioning analysis" "messaging comparison across competitors" "content gap analysis" "battlecard for Stripe" ``` **How it works:** Loads the competitor list from the business profile (limits to 4 with API credit estimates). Captures your own positioning as a baseline, then researches competitors in parallel -- extracting homepage/features/pricing, analyzing blog content, and reviewing social proof. Compares against prior snapshots. **Output includes:** ### Full Snapshot (first run or 14+ days since last) * TL;DR for marketing (3-5 actionable insights) * Messaging matrix (tagline, primary CTA, value props, target audience, pricing model) with verbatim quotes * Per-competitor positioning profile * Content gap analysis and positioning white space * Recommended actions ### Delta Mode (under 14 days since last run) * Before/after per competitor (tagline shifts, new features, pricing changes, blog themes) * Marketing implications ### Battlecard Mode Triggered by `"battlecard [competitor]"`. Produces a structured document with competitor overview, key claims with source URLs, counter-positioning, feature comparison, objection handling, and talking points. # Overview Source: https://docs.nimbleway.com/integrations/agent-skills/web-search-skills/overview One-command web intelligence workflows for any AI agent One-command workflows that turn any AI agent into a web intelligence powerhouse. Web Search Skills spawn parallel sub-agents, gather live web data via Nimble APIs, synthesize findings, and deliver structured reports with dates and source URLs. Compatible with any AI agent or platform that supports the [Agent Skills](https://agentskills.io) protocol -- including [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Cursor](https://www.cursor.com/), and the [Vercel Agent Skills CLI](https://www.npmjs.com/package/skills). Powered by the [Nimble CLI](/nimble-sdk/sdks/cli). Source code for all skills ## Skills Categories 360° company research, competitor monitoring, and market discovery Track competitor positioning, messaging shifts, pricing changes, and content gaps Meeting prep with attendee research, and local business discovery Live SEO intelligence from keyword research to AI search visibility ### Individual Skills Web Search Skills also extend into vertical-specific workflows: | Category | Skill | Description | | - | - | - | | [Business Research](/integrations/agent-skills/web-search-skills/business-research) | [Company Deep Dive](https://github.com/Nimbleway/agent-skills/tree/main/skills/company-deep-dive) | Sourced 360° report on any company | | | [Competitor Intel](https://github.com/Nimbleway/agent-skills/tree/main/skills/competitor-intel) | Monitor competitor news, funding, hiring, and product launches | | | [Market Finder](https://github.com/Nimbleway/agent-skills/tree/main/skills/market-finder) | Discover companies in any market or segment | | [Marketing](/integrations/agent-skills/web-search-skills/marketing) | [Competitor Positioning](https://github.com/Nimbleway/agent-skills/tree/main/skills/competitor-positioning) | Track messaging shifts, pricing changes, and content gaps | | [Productivity](/integrations/agent-skills/web-search-skills/productivity) | [Meeting Prep](https://github.com/Nimbleway/agent-skills/tree/main/skills/meeting-prep) | Research attendees and their companies before any meeting | | | [Local Places](https://github.com/Nimbleway/agent-skills/tree/main/skills/local-places) | Discover local businesses in any neighborhood | | [SEO](/integrations/agent-skills/web-search-skills/seo) | [SEO Intel](https://github.com/Nimbleway/agent-skills/tree/main/skills/seo-intel) | Live SEO intelligence across keyword research, rank tracking, site audits, content gaps, competitor keywords, AI visibility, and GitHub SEO | ## How It Works Under the hood, Web Search Skills intelligently orchestrate Nimble's [Web Tools](/integrations/agent-skills/web-tools-skills/nimble-web-expert) -- [Search](/nimble-sdk/web-tools/search), [Extract](/nimble-sdk/web-tools/extract/quickstart), [Map](/nimble-sdk/web-tools/map), and [Crawl](/nimble-sdk/web-tools/crawl) -- to fetch data from the web as needed. They also discover and trigger the best [templates](/nimble-sdk/web-tools/extract/template) for the task at hand, choosing from hundreds of pre-built Extract Templates optimized for specific websites and data types. For bulk operations like table enrichments, skills automatically create batch background tasks -- processing rows in parallel without blocking the conversation. Every Web Search Skill follows the same pattern: Checks the Nimble CLI is installed and loads the business profile. Understands the task, asks clarifying questions, and aligns on scope before starting. Spawns sub-agents that orchestrate Nimble Web Tools for concurrent data gathering across multiple web sources. Synthesizes findings and deduplicates against previous runs. Delivers structured output with verified dates and source URLs. Detects connected integrations (Notion, Slack, Google Drive, and more) and proposes where to store the report. Can also generate interactive apps or trigger follow-up skills. Skills learn from previous runs and only surface what's new. Run the same skill twice and the second run highlights changes, not repeats. ## Private Local Web Index Skills maintain a local web knowledge index at `~/.nimble/memory/` -- live web intelligence that compounds across sessions, so your agent never starts from scratch. * **Local-first indexes** -- per-directory entity catalogs for instant lookup, no vector DB needed * **[Obsidian](https://obsidian.md/)-compatible** -- `[[wiki links]]` cross-reference people, companies, and competitors. Open `~/.nimble/memory/` in Obsidian to browse the intelligence graph * **Cross-entity synthesis** -- patterns across competitors, pricing trends, and market signals surface automatically * **Ad-hoc insights** -- say "remember this" mid-conversation and it compounds into the right entity page * **Activity log** -- grep-friendly record of what was learned, when, and by which skill Every finding carries a verified event date and source URL. Stale signals are dropped, not reported. ## Quick Install Installs the Nimble skills and configures the MCP server. ```bash theme={"system"} claude plugin marketplace add Nimbleway/agent-skills && \ claude plugin install nimble@nimble-plugin-marketplace ``` Run `/mcp` and confirm `nimble` appears in your connected servers. One click, then confirm the install in Cursor. Add to Cursor Then replace `NIMBLE_API_KEY` in **Settings → MCP Servers → nimble-mcp-server** with your [Nimble API key](https://online.nimbleway.com/settings/api-keys). The deeplink ships a placeholder, not a key. Teaches the agent which Nimble product fits a given job. ```bash theme={"system"} npx skills add Nimbleway/agent-skills -a cursor ``` Open Settings, then MCP, and confirm `nimble-mcp-server` is listed. Installs the Nimble skills into your project. ```bash theme={"system"} npx skills add Nimbleway/agent-skills ``` Run `npx skills add Nimbleway/agent-skills --list` to see what was installed. ## Prerequisites Requires the [Nimble CLI](/nimble-sdk/sdks/cli) and a [Nimble API key](https://online.nimbleway.com/settings/api-keys). See [Plugin Installation](/integrations/agent-skills/plugin-installation) for full setup instructions. # Productivity Source: https://docs.nimbleway.com/integrations/agent-skills/web-search-skills/productivity Meeting prep with attendee research, and local business discovery Walk into meetings fully briefed and discover local businesses in any neighborhood. These skills gather live web data and deliver structured, actionable results. For shared architecture, install instructions, and the Private Local Web Index, see the [Web Search Skills overview](/integrations/agent-skills/web-search-skills/overview). ## Meeting Prep Source code for this skill Researches meeting attendees and their companies before any meeting. Surfaces roles, recent activity, company context, talking points, and cross-attendee relationships. **Example prompts:** ``` "prepare me for my meeting with Sarah Chen from Acme Corp" "who am I meeting with tomorrow" "brief me on the attendees for my 2pm meeting" ``` **How it works:** Accepts attendee names and companies directly, or parses them from a calendar invite. Researches each person's role, background, recent activity, and social presence. Gathers company context (news, funding, product launches). Maps cross-attendee connections. **Output includes:** * Per-attendee profile (role, background, recent activity, published content) * Company context per organization * Talking points based on shared interests and recent activity * Cross-attendee relationships * Source URLs *** ## Local Places Source code for this skill Discovers, enriches, and scores local businesses in any neighborhood. Returns a structured, ranked list with an optional interactive map. **Example prompts:** ``` "find all coffee shops in SoHo" "map every bar in Austin's 6th Street" "build a neighborhood guide for Williamsburg, Brooklyn" ``` **How it works:** Identifies business type and location, then searches across Google Maps, Yelp, BBB, and vertical-specific directories in parallel. Enriches each business with reviews, ratings, social presence, and contact info. Assigns confidence scores based on source count and review volume. **Output includes:** * Ranked business list with name, address, phone, website, rating, review count, and social links * Confidence scores per business * Interactive HTML map (optional) * Source links # SEO Source: https://docs.nimbleway.com/integrations/agent-skills/web-search-skills/seo Live SEO intelligence from keyword research to AI search visibility Answer any SEO question about a website with live data, not checklist advice. This skill routes natural language SEO queries to the right workflow, gathers evidence from live SERPs and real pages, and delivers structured reports with source URLs. For shared architecture, install instructions, and the Private Local Web Index, see the [Web Search Skills overview](/integrations/agent-skills/web-search-skills/overview). ## SEO Intel Source code for this skill A single SEO skill that covers the full lifecycle from keyword research to AI search visibility. Detects intent from natural language and dispatches to one of seven workflows. Every finding is grounded in live web data, not LLM guesses. **Example prompts:** ``` "find keyword opportunities for my SaaS product" "run an SEO audit on example.com" "check our AI visibility vs competitors" "track rankings for these 5 keywords" "what content gaps do we have vs competitor.com" "audit the SEO on our GitHub repo" ``` **How it works:** Parses the request and classifies it against seven workflow intents. Loads the matching workflow reference, gathers live data via Nimble Search, Extract, Map, Crawl, or dedicated AI platform agents, then synthesizes a structured report with evidence and source URLs. Compares against prior runs in the local memory index to surface only what changed. **Output includes:** * TL;DR with the top findings and recommended actions * Per-workflow sections with dated, sourced evidence * Confidence tiers on every finding * Source URLs for every claim * Delta mode across runs (what changed since last time) ### Workflows | Workflow | What it does | Example trigger | | - | - | - | | Keyword Research | Discovers opportunities via live SERP analysis, evidence-based difficulty scoring, and GEO/AEO surface classification | `"find keyword opportunities for my SaaS product"` | | Rank Tracker | Tracks live SERP positions with JSON snapshots, smart date windowing, and delta reports across runs | `"track rankings for these 5 keywords"` | | Site Audit | Full-crawl technical and on-page audit with JavaScript rendering, structured metadata parsing, and confidence-tiered findings | `"run an SEO audit on example.com"` | | Content Gap | Parallel domain mapping, topic coverage comparison, and tiered SERP validation | `"what content gaps do we have vs competitor.com"` | | Competitor Keywords | Full-site crawl with body-only anchor extraction, theme clustering, and per-domain delta snapshots | `"reverse-engineer competitor.com's SEO"` | | AI Visibility | Brand visibility measurement across ChatGPT, Perplexity, Google AI, Gemini, and Grok via dedicated agents | `"check our AI visibility vs competitors"` | | GitHub SEO | Repository discoverability audit covering metadata, README quality, community health, and AI discoverability | `"audit the SEO on our GitHub repo"` | ### AI Visibility Measures brand presence where users increasingly start their searches: AI answer engines. Queries five dedicated agents in parallel -- ChatGPT, Perplexity, Google AI Overview, Gemini, and Grok -- then attributes mentions, citations, and source URLs per platform. Applies Princeton's GEO optimization methodology to recommend content changes that improve citation likelihood. ### Workflow Chaining Workflows feed into each other. The skill suggests natural next steps after every run: * **Site Audit → Keyword Research → Content Gap → Rank Tracker** (full SEO setup) * **Keyword Research → Competitor Keywords → Content Gap** (editorial planning) * **AI Visibility → Content Gap → Keyword Research** (generative search optimization) Context carries across runs -- discovered domains, keyword lists, and business profile data do not get re-asked. # Nimble Web Expert Source: https://docs.nimbleway.com/integrations/agent-skills/web-tools-skills/nimble-web-expert Turn your AI coding assistant into a web scraping expert The Nimble Web Expert skill gives your agent the ability to search, extract, scrape, map, crawl, and run pre-built Extract Templates across any website. Fast, incremental, and immediately responsive. This is the only way your AI assistant can access live websites. Powered by the [Nimble CLI](https://www.npmjs.com/package/@nimble-way/nimble-cli) and built on the open-source [Agent Skills](https://agentskills.io) standard for cross-platform agent compatibility. Source code for this skill ## Tools Overview | Tool | Description | | - | - | | **Search** | Accurate, real-time web search. 8 focus modes: general, coding, news, academic, shopping, social, geo, location. | | **Extract** | Scalable data collection with stealth unblocking. Get clean, real-time HTML and structured data from any URL. Supports JS rendering and browser emulation. | | **Map** | Fast URL discovery and site structure mapping. Plan extraction workflows before running them. | | **Crawl** | Extract contents from entire websites in a single request. Collect large volumes of web data automatically. | | **Extract Templates** | Run pre-built Extract Templates for structured data from popular websites (Amazon, Google, LinkedIn, and hundreds more). | ## Quick Install Installs the Nimble skills and configures the MCP server. ```bash theme={"system"} claude plugin marketplace add Nimbleway/agent-skills && \ claude plugin install nimble@nimble-plugin-marketplace ``` Run `/mcp` and confirm `nimble` appears in your connected servers. One click, then confirm the install in Cursor. Add to Cursor Then replace `NIMBLE_API_KEY` in **Settings → MCP Servers → nimble-mcp-server** with your [Nimble API key](https://online.nimbleway.com/settings/api-keys). The deeplink ships a placeholder, not a key. Teaches the agent which Nimble product fits a given job. ```bash theme={"system"} npx skills add Nimbleway/agent-skills -a cursor ``` Open Settings, then MCP, and confirm `nimble-mcp-server` is listed. Installs the Nimble skills into your project. ```bash theme={"system"} npx skills add Nimbleway/agent-skills ``` Run `npx skills add Nimbleway/agent-skills --list` to see what was installed. ## Prerequisites ### 1. Nimble CLI Install the Nimble CLI globally: ```bash theme={"system"} npm i -g @nimble-way/nimble-cli ``` Verify with: ```bash theme={"system"} nimble --version ``` ### 2. Nimble API Key Get your key at [online.nimbleway.com](https://online.nimbleway.com/). Set the `NIMBLE_API_KEY` environment variable using your platform's method: Add to `~/.claude/settings.json`: ```json theme={"system"} { "env": { "NIMBLE_API_KEY": "your-api-key-here" } } ``` ```bash theme={"system"} export NIMBLE_API_KEY="your-api-key-here" ``` Or add to your shell profile (`~/.bashrc`, `~/.zshrc`): ```bash theme={"system"} echo 'export NIMBLE_API_KEY="your-api-key-here"' >> ~/.zshrc ``` * Add skills to `.github/skills/` in your repository * Configure API key using GitHub Actions secrets in the copilot environment * Or set as environment variable in your shell ## Quick Start The Nimble Web Expert skill activates automatically when you ask for live web data: ``` "Search for recent AI developments" -> nimble search --query "recent AI developments" --search-depth lite "Extract the content from this URL" -> nimble extract run --url "https://example.com" --parse --format markdown "Scrape prices from this product page" -> nimble extract run --url "https://example.com/product" --render --parse --format markdown "Map all the pages on this docs site" -> nimble map --url "https://docs.example.com" --limit 100 "Crawl the API docs section" -> nimble crawl run --url "https://docs.example.com/api" --limit 50 ``` Use this skill for immediate, one-off data needs. For workflows you will run repeatedly, have it build an [Extract Template](/nimble-sdk/web-tools/extract/template) or schedule an [Extract Job](/nimble-sdk/agentic/jobs) instead. ## Command Examples ### Search Accurate, real-time web search with 8 focus modes (general, coding, news, academic, shopping, social, geo, location). ```bash theme={"system"} # Lite search (default, fast, token-efficient) nimble search --query "React hooks tutorial" --focus coding --search-depth lite # Search with AI-generated answer summary nimble search --query "what is WebAssembly" --include-answer --search-depth lite # News search with time filter nimble search --query "AI developments" --focus news --time-range week --search-depth lite # Domain-filtered search nimble search --query "auth best practices" \ --include-domain github.com \ --include-domain stackoverflow.com \ --search-depth lite ``` Use `--search-depth lite` (default) for fast responses (1-3s). Use `--search-depth deep` when you need full page content for archiving or full-text analysis. ### Extract Get clean, structured data from any URL with stealth unblocking and JS rendering. ```bash theme={"system"} # Standard extraction (always use --parse --format markdown) nimble extract run --url "https://example.com/article" --parse --format markdown # Render JavaScript for SPAs and dynamic content nimble extract run --url "https://example.com/app" --render --parse --format markdown # Extract with geolocation nimble extract run --url "https://example.com" --country US --city "New York" --parse --format markdown ``` ### Map Discover all URLs on a site to plan extraction workflows. ```bash theme={"system"} # Map URLs on a site nimble map --url "https://docs.example.com" --limit 100 # Include subdomains nimble map --url "https://example.com" --domain-filter subdomains ``` ### Crawl Extract content from entire websites asynchronously. ```bash theme={"system"} # Start a crawl (always set --limit) nimble crawl run --url "https://docs.example.com" --limit 50 # Crawl with path filtering nimble crawl run --url "https://example.com" \ --include-path "/docs" \ --include-path "/api" \ --limit 100 # Check crawl status nimble crawl status --id "crawl-id" # List all crawls nimble crawl list ``` For LLM-friendly output, prefer `map` + `extract --parse --format markdown` on individual pages. Crawl returns raw HTML which can be very large. ## One-off data vs reusable workflows This skill covers both, so the choice is which Nimble product it reaches for: | | **Immediate data** | **Reusable workflow** | | - | - | - | | **Reach for** | Search, Extract, Map, Crawl | [Extract Template](/nimble-sdk/web-tools/extract/template), [Extract Jobs](/nimble-sdk/agentic/jobs) | | **Speed** | Fast and direct | Slower to set up, then repeatable | | **Output** | Extracted data now | A template or a scheduled job | | **Use when** | "Get me this data now" | "I'll need this repeatedly" | For open-ended research with citations rather than extraction, use an [Agent](/nimble-sdk/web-search-agents/overview). ## About Agent Skills This skill follows the [Agent Skills](https://agentskills.io) open-source standard, making it compatible with multiple AI agent platforms. Install using the [Skills CLI](https://www.npmjs.com/package/skills), the standard package manager for the Agent Skills ecosystem. ## Full Command Reference For the complete list of options, flags, and advanced usage patterns, see the [SKILL.md on GitHub](https://github.com/Nimbleway/agent-skills/blob/main/skills/nimble-web-expert/SKILL.md). # Claude AI Connectors Source: https://docs.nimbleway.com/integrations/connectors/anthropic/claude-connectors Add Nimble to Claude.ai as a custom connector for real-time web search, extraction, and crawling. Add Nimble to Claude.ai as a custom connector and Claude can search, extract, map, and crawl the web on its own — no code required. Cowork new-task input with the + menu open: Connectors submenu shows Nimble MCP toggled on alongside Excalidraw, Google Drive, Linear, Notion, Slack, and Claude in Chrome ## Supported Surfaces Custom connectors work across Anthropic's products: * **Claude.ai** (web) * **Claude Desktop** (macOS, Windows) * **Claude Code** * **Claude Cowork** ## Prerequisites * A [Claude.ai](https://claude.ai) account on a plan that supports connectors (Pro, Max, Team, or Enterprise). * A [Nimble account](https://online.nimbleway.com/signup) — sign in via OAuth during setup. No API key needed. ## Installation In Claude.ai, navigate to **Customize > Connectors**. Click the **+** button, then choose **Add custom connector**. Customize > Connectors page with the + menu open, showing the Add custom connector option Give the connector a name (e.g. `Nimble MCP`) and set the remote MCP server URL to: ``` https://mcp.nimbleway.com/mcp ``` Add custom connector dialog with name 'Nimble MCP' and URL https://mcp.nimbleway.com/mcp Click **Add**. The Nimble connector now appears in your connectors list with a `CUSTOM` badge. Connectors list showing Nimble MCP with a CUSTOM badge Open the new **Nimble MCP** connector and click **Connect**. Nimble MCP connector page showing 'You are not connected to Nimble MCP yet.' with a Connect button Review the permissions Claude is requesting and click **Approve** to authenticate with your Nimble workspace. Once approved, the connector is live inside Claude conversations. OAuth approval dialog: Claude is requesting access to Nimble with permissions to verify identity, search the web, extract data, manage Web Search Agents, and maintain access offline For Anthropic's full reference on custom connectors, see the [Claude support article](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp). ## Tools Once connected, Claude calls any of the Nimble MCP server's tools automatically, based on the user's prompt. | Tool | Use Case | | - | - | | `nimble_search` | Web search across multiple engines with configurable depth | | `nimble_extract` | Pull clean content from any URL | | `nimble_map` | Discover all URLs on a domain | | `nimble_crawl` | Crawl an entire site with path filtering | | `nimble_agents_run` | Run a custom Agent | For the full list of MCP tools and parameters, see the [Nimble MCP Server reference](/integrations/mcp-server/mcp-server). ## Power Your Flow with Nimble Agent Skills Nimble is an **official Anthropic plugin partner**. The Nimble plugin ships in Claude's **Anthropic & Partners** directory and bundles the MCP server with a curated set of agent skills — opinionated workflows that guide Claude through the most common web data jobs (research, scraping, mapping, and crawling) so you get the best of Nimble's Web Tools without writing a prompt from scratch. Once installed, just type `/` in any new task to invoke a skill directly: Cowork new-task input with /nimble-web-expert prepended to a prompt: 'Find the top 5 AI agent platforms launched this year and summarize their pricing pages.' ### Install the Nimble plugin From either the **Cowork** or **Claude Code** tab in Claude Desktop, click **Customize** in the left sidebar, then click **Browse plugins** to open the Directory. Switch to the **Plugins** tab, select **Anthropic & Partners**, and search for `Nimble`. Directory with the Plugins tab and Anthropic & Partners filter selected, showing the Nimble plugin card Click the Nimble card to open its details, then click **Install**. The plugin saves locally on your machine and the bundled MCP server, agent skills, and sub-agents register automatically. Nimble plugin detail page showing 20 skills, 2 agents, and the nimble-mcp-server connector, with an Install button Once installed, Nimble's agent skills are available across your conversations. Type `/` in chat to browse them, or let Claude invoke them automatically based on your prompt. Installed Nimble plugin page listing skills like /nimble-web-expert, /company-deep-dive, /competitor-intel, /local-places, /meeting-prep, and /nimble-agent-builder For Anthropic's full reference, see [Use plugins in Claude Cowork](https://support.claude.com/en/articles/13837440-use-plugins-in-claude-cowork). ## How It Works Type a prompt that needs live web data. Claude detects when the Nimble connector can help. Claude routes the request to the Nimble MCP server, which runs the search, extraction, or crawl in real time. Results return to the conversation as structured data Claude can quote, cite, or summarize. ## Example Prompts > *"Find the top 5 AI agent platforms launched this year and summarize their pricing pages."* Claude calls `nimble_search` for the list, then `nimble_extract` on each pricing page, then summarizes. > *"Crawl docs.example.com and list every page that mentions 'rate limits.'"* Claude calls `nimble_crawl` with a path filter, then surfaces matches with URLs. > *"Pull the headline, author, and publish date from this article: `https://example.com/article`"* Claude calls `nimble_extract` and returns the structured fields. > *"Show me every page on nimbleway.com under /blog."* Claude calls `nimble_map` and lists the discovered URLs. ## Next Steps Full MCP server reference — Cursor, Claude Code, and Claude Desktop setup Use Nimble as a tool inside the Anthropic SDK in Python or Node Web search with depth levels, filtering, and AI answers Extract clean content from any URL # Anthropic SDK Source: https://docs.nimbleway.com/integrations/connectors/anthropic/sdk Use the Nimble SDK as a tool inside Claude's tool-use API. Give Claude models real-time web data by wiring the Nimble SDK into Anthropic tool use. No extra packages needed. ## Prerequisites ```bash Python theme={"system"} pip install anthropic nimble_python ``` ```bash Node theme={"system"} npm install @anthropic-ai/sdk @nimble-way/nimble-js ``` Set environment variables: ```bash theme={"system"} export ANTHROPIC_API_KEY="your-anthropic-api-key" export NIMBLE_API_KEY="your-nimble-api-key" ``` Get a Nimble API key from the [dashboard](https://online.nimbleway.com/settings/api-keys) (free trial available). The examples set the Nimble client's `client_source` (`clientSource` in Node) to `anthropic-sdk`, which sends an `X-Client-Source` header that attributes requests to this integration. It defaults to `sdk` when unset. ## Quick Start: Tool Runner The Anthropic Python SDK includes a `tool_runner` that handles the tool-calling loop automatically. Define Nimble tools with the `@beta_tool` decorator and the SDK manages execution, message history, and retries. ```python Python theme={"system"} import os import json import anthropic from anthropic import beta_tool from nimble_python import Nimble client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"]) nimble_client = Nimble(api_key=os.environ["NIMBLE_API_KEY"], client_source="anthropic-sdk") @beta_tool def nimble_search(query: str) -> str: """Search the web using Nimble and return relevant results. Args: query: The search query to execute. """ result = nimble_client.search(query=query) return json.dumps(result, default=str) @beta_tool def nimble_extract(url: str) -> str: """Extract clean content from a URL using Nimble. Args: url: The URL to extract content from. """ result = nimble_client.extract(url=url) return json.dumps(result, default=str) runner = client.beta.messages.tool_runner( model="claude-sonnet-5", max_tokens=4096, tools=[nimble_search, nimble_extract], messages=[{"role": "user", "content": "What are the latest trends in AI agents?"}], ) for message in runner: for block in message.content: if hasattr(block, "text"): print(block.text) ``` The `@beta_tool` decorator auto-generates the JSON schema from type hints and doc strings, so no manual schema definition is needed. ## Agent API V2: autonomous research tool The tools above (`search`, `extract`, `crawl`, `map`, and [Extract Templates](/nimble-sdk/web-tools/extract/template)) are **synchronous and single-shot**. One call returns data directly, and Claude does the reasoning. [Agent API V2](/nimble-sdk/web-search-agents/overview) is different. It exposes an **asynchronous research agent** that plans, searches across many sources, and returns a synthesized answer with a per-claim [trust report](/nimble-sdk/web-search-agents/trust). The lifecycle is **create a run → poll to a terminal state → retrieve the result**. Wrap that whole lifecycle in one function so Claude sees a single "deep research" tool. ### 1. Wrap the run lifecycle This helper uses a stable `agent_name`, so every call — from this process or any other — reuses the same agent and its memory instead of spinning up a fresh one each time. It creates the run, polls until terminal, handles `failed` and `cancelled` runs, and returns the answer plus its trust and citation metadata. Errors return a safe message, so no API key or raw exception is surfaced. ```python Python theme={"system"} import os import time from nimble_python import Nimble nimble_client = Nimble(api_key=os.environ["NIMBLE_API_KEY"], client_source="anthropic-sdk") # Stable name for this integration's research agent. The first call creates # it; every later call (here or from another process) reuses it and its memory. # Pick a name unique to this deployment - anyone reusing the same name on the # same account reuses the same agent and its memory. NIMBLE_AGENT_NAME = os.environ["NIMBLE_AGENT_NAME"] def run_deep_research(query: str, timeout_s: int = 300) -> dict: """Run one Agent API V2 research task end to end and return a cited answer.""" try: # Create-or-reuse the named agent, then start the run. run = nimble_client.agents.run(input=query, agent_name=NIMBLE_AGENT_NAME) agent_id = run.web_search_agent_id # Poll until the run reaches a terminal state. deadline = time.time() + timeout_s while run.is_active: if time.time() > deadline: return {"status": "timeout", "error": "Run did not finish in time."} time.sleep(10) run = nimble_client.agents.runs.get(run.id, agent_id=agent_id) # Handle failed or cancelled runs. if run.status != "completed": return {"status": run.status, "error": f"Run ended as '{run.status}'."} # Retrieve the completed result and summarize its trust report. result = nimble_client.agents.runs.result(run.id, agent_id=agent_id) output = result.output return { "status": "completed", "answer": output.content, "confidence": output.trust.confidence, "citations": [ {"url": s.url, "title": s.title, "source_type": s.type} for s in output.trust.sources ], } except Exception: # Never leak credentials or raw exceptions back to the model. return {"status": "error", "error": "Nimble request failed."} ``` Pass an `output_schema` to `agents.run` (or `agents.runs.create`) to get structured JSON in `result.output` instead of prose. Trust is then keyed by JSON path. See [Dataset Building](/nimble-sdk/web-search-agents/use-cases/dataset-building). ### 2. Expose it with the Tool Runner Wrap the helper with `@beta_tool` and pass it to `tool_runner`. Claude calls one tool; the async run loop stays hidden. ```python Python theme={"system"} import os import json import anthropic from anthropic import beta_tool client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"]) @beta_tool def nimble_deep_research(query: str) -> str: """Run an autonomous, fully-cited web-research task with Nimble Agent API V2. Use for open-ended questions that need synthesis across multiple sources, not single-page lookups. Returns a cited answer with a trust grade. Args: query: The research question or task, in plain language. """ return json.dumps(run_deep_research(query), default=str) runner = client.beta.messages.tool_runner( model="claude-sonnet-5", max_tokens=4096, tools=[nimble_deep_research], messages=[{ "role": "user", "content": "Compare the pricing and positioning of Datadog and Grafana Cloud.", }], ) for message in runner: for block in message.content: if hasattr(block, "text"): print(block.text) ``` The tool returns the synthesized answer, an overall `confidence` grade (`high`, `medium`, or `low`), and the source URLs behind it, so Claude can weigh how much to trust each claim. See [Trust](/nimble-sdk/web-search-agents/trust) for the full report structure. ## Messages API (Manual Loop) For more control, define tools using the Anthropic `input_schema` format and handle the `tool_use` → `tool_result` loop manually. ### 1. Define the Tool Schema ```python Python theme={"system"} tools = [ { "name": "nimble_search", "description": "Search the web using Nimble and return relevant results.", "input_schema": { "type": "object", "properties": { "query": { "type": "string", "description": "The search query to execute" } }, "required": ["query"] } }, { "name": "nimble_extract", "description": "Extract clean content from a URL using Nimble.", "input_schema": { "type": "object", "properties": { "url": { "type": "string", "description": "The URL to extract content from" } }, "required": ["url"] } } ] ``` ### 2. Handle the Tool-Use Loop ```python Python theme={"system"} import os import json import anthropic from nimble_python import Nimble client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"]) nimble_client = Nimble(api_key=os.environ["NIMBLE_API_KEY"], client_source="anthropic-sdk") def process_tool_call(name, tool_input): if name == "nimble_search": result = nimble_client.search(query=tool_input["query"]) return json.dumps(result, default=str) elif name == "nimble_extract": result = nimble_client.extract(url=tool_input["url"]) return json.dumps(result, default=str) messages = [ {"role": "user", "content": "What are the latest trends in AI agents?"} ] response = client.messages.create( model="claude-sonnet-5", max_tokens=4096, tools=tools, messages=messages, ) # Tool-use loop: keep processing until Claude stops calling tools while response.stop_reason == "tool_use": tool_use_blocks = [block for block in response.content if block.type == "tool_use"] messages.append({"role": "assistant", "content": response.content}) tool_results = [] for tool_use in tool_use_blocks: result = process_tool_call(tool_use.name, tool_use.input) tool_results.append({ "type": "tool_result", "tool_use_id": tool_use.id, "content": result, }) messages.append({"role": "user", "content": tool_results}) response = client.messages.create( model="claude-sonnet-5", max_tokens=4096, tools=tools, messages=messages, ) for block in response.content: if hasattr(block, "text"): print(block.text) ``` ### Node.js Example ```typescript Node theme={"system"} import Anthropic from "@anthropic-ai/sdk"; import Nimble from "@nimble-way/nimble-js"; const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }); const nimble = new Nimble({ apiKey: process.env.NIMBLE_API_KEY, clientSource: "anthropic-sdk" }); const tools: Anthropic.Tool[] = [ { name: "nimble_search", description: "Search the web using Nimble and return relevant results.", input_schema: { type: "object" as const, properties: { query: { type: "string", description: "The search query to execute" }, }, required: ["query"], }, }, { name: "nimble_extract", description: "Extract clean content from a URL using Nimble.", input_schema: { type: "object" as const, properties: { url: { type: "string", description: "The URL to extract content from" }, }, required: ["url"], }, }, ]; async function processToolCall(name: string, toolInput: Record) { if (name === "nimble_search") { return await nimble.search({ query: toolInput.query }); } if (name === "nimble_extract") { return await nimble.extract({ url: toolInput.url }); } } const messages: Anthropic.MessageParam[] = [ { role: "user", content: "What are the latest trends in AI agents?" }, ]; let response = await client.messages.create({ model: "claude-sonnet-5", max_tokens: 4096, tools, messages, }); while (response.stop_reason === "tool_use") { const toolUseBlocks = response.content.filter( (block): block is Anthropic.ToolUseBlock => block.type === "tool_use" ); messages.push({ role: "assistant", content: response.content }); const toolResults: Anthropic.ToolResultBlockParam[] = []; for (const toolUse of toolUseBlocks) { const result = await processToolCall( toolUse.name, toolUse.input as Record ); toolResults.push({ type: "tool_result", tool_use_id: toolUse.id, content: JSON.stringify(result), }); } messages.push({ role: "user", content: toolResults }); response = await client.messages.create({ model: "claude-sonnet-5", max_tokens: 4096, tools, messages, }); } for (const block of response.content) { if (block.type === "text") { console.log(block.text); } } ``` ## How It Works The Anthropic tool-use flow has three steps: Pass the tool definitions and user message to Claude. If Claude decides to use a tool, it returns a `tool_use` content block with the tool name and input. Parse the `tool_use` block and call the corresponding Nimble SDK method. Return the result as a `tool_result` message. Claude processes the tool result and responds with a text answer. If it needs more data, it may call another tool, and the loop continues until `stop_reason` is `end_turn`. ## Available Tools Any Nimble SDK method can be exposed as an Anthropic tool. Here are the most common ones: | Tool | SDK Method | Use Case | | - | - | - | | `nimble_search` | `client.search()` | Web search with structured results | | `nimble_extract` | `client.extract()` | Extract content from a URL | | `nimble_crawl` | `client.crawl.run()` | Crawl an entire site | | `nimble_map` | `client.map()` | Discover all URLs on a domain | See the [Python SDK](/nimble-sdk/sdks/python) and [Node SDK](/nimble-sdk/sdks/node) docs for the full list of methods and parameters. ## Next Steps Full Python SDK reference with all methods and configuration options Full Node.js SDK reference with TypeScript support Agent API V2: autonomous research runs with per-claim trust Use Nimble with OpenAI function calling and the Agents SDK # Cloudflare Agents SDK Source: https://docs.nimbleway.com/integrations/connectors/cloudflare-agents-sdk Run Nimble Agent research from a Cloudflare Worker. Durable Object-backed start, status, and result tools survive a restart without losing the run. ## Overview Give a Cloudflare Worker the ability to research the live web. Ask a question in one sentence. Get back a written answer with sources, per-claim citations, and a confidence grade. The [`@nimble-way/cloudflare-agents`](https://www.npmjs.com/package/@nimble-way/cloudflare-agents) package brings [Nimble Agent](/nimble-sdk/web-search-agents/overview) research into the [Cloudflare Agents SDK](https://developers.cloudflare.com/agents/). Nimble plans the research, searches, and opens pages. It reads JavaScript-heavy sites, cross-checks what it finds, and returns a cited answer. Your Worker sends a task and collects the result. That research takes minutes, not milliseconds. A `high` effort run takes 5 to 15 minutes, far longer than any Worker request. So the package parks the run in a Durable Object and hands you a key to collect it later. Without this, you would store the run ID in Durable Object storage, set an alarm to poll it, and build your own create-idempotency so a retry does not bill twice. * **A cited answer from the live open web**: prose or JSON, with sources, per-claim citations, and confidence grades, read at run time. * **Gets cheaper the more you run it**: a reused agent remembers which sources worked. Recurring research runs up to 50% cheaper. * **Start now, collect later**: start in one request, read the result in another. An eviction or a redeploy resumes the same run rather than creating a new one. * **Never bills you twice**: within one agent instance, the same request can never start a second run. ### Which path to take Two ways to get Nimble into a Cloudflare agent. Pick one in ten seconds. The fastest start. No package, no Durable Object binding, no migration. Tools arrive over the wire and the model calls them. Best for interactive agents and short tasks. For runs that must survive. Run identity lives in durable storage and resumes after a restart or a redeploy. Best when losing a 15-minute research run is unacceptable. ## Prerequisites A Wrangler version that supports SQLite Durable Objects, and a Workers account with Durable Objects enabled. The agent binds as a SQLite Durable Object, so the project needs a `new_sqlite_classes` migration. `agents` is a peer dependency, range `>=0.20.0 <1`. Your project supplies it. The package uses `startFiber()` and `onFiberRecovered()`, which land in `0.20.0`. Required for the build and test toolchain. The published code targets the Workers runtime, not Node. Get a key from the [dashboard](https://online.nimbleway.com/settings/api-keys). Store it with `wrangler secret put`. See [Handling the API key](#handling-the-api-key) for the rules that keep it out of model input. Pass `nimbleAgentId` to run against a Nimble Agent you already created, for example `wsa_01j9x8...`. Or pass `agent_name` in the request to create one on first use and reuse it by name after that. Pass one of them for anything that runs more than once. A reused agent keeps its memory, so it gets faster, more accurate, and up to 50% cheaper on recurring work. Omit both and Nimble provisions a fresh, disposable agent for that run. No memory carries over, so every run starts from zero at full price. Both paths return a Nimble Agent ID you can read back. Reusing the agent is the Nimble half. For scheduled work, see [Scheduled and recurring runs](#scheduled-and-recurring-runs) for the Cloudflare half. ## Quick Start ```bash theme={"system"} npm install @nimble-way/cloudflare-agents@0.1.0 agents ``` `0.1.0` is the current release. ```bash theme={"system"} wrangler secret put NIMBLE_API_KEY ``` Never put the key in `wrangler.jsonc`, in a `vars` block, or in a committed `.dev.vars`. ```jsonc wrangler.jsonc theme={"system"} { "name": "my-research-worker", "main": "src/index.ts", "compatibility_date": "2026-09-01", "compatibility_flags": ["nodejs_compat"], "durable_objects": { "bindings": [ { "name": "NIMBLE_RUN_AGENT", "class_name": "NimbleRunAgent" } ] }, "migrations": [ { "tag": "v1", "new_sqlite_classes": ["NimbleRunAgent"] } ] } ``` The binding name must be `NIMBLE_RUN_AGENT`. The package reads it from `env`. `nodejs_compat` is required, not optional. The Agents SDK imports `node:async_hooks` for the `AsyncLocalStorage` behind every fiber. Without the flag `wrangler deploy` fails to resolve built-in Node modules. ```ts src/index.ts theme={"system"} import { getAgentByName } from "agents"; import type { Env as NimbleEnv } from "@nimble-way/cloudflare-agents"; export { NimbleRunAgent } from "@nimble-way/cloudflare-agents"; interface Env extends NimbleEnv {} export default { async fetch(request: Request, env: Env): Promise { const url = new URL(request.url); // One instance per trusted principal. See the warning below. const runAgent = await getAgentByName(env.NIMBLE_RUN_AGENT, "research-session"); if (url.pathname === "/start") { const outcome = await runAgent.startRun({ request: { input: "Summarize how the EU AI Act is being enforced in 2026, with sources.", effort: "high", use_case: "research", }, }); return Response.json(outcome); } const fiberKey = url.searchParams.get("key"); if (!fiberKey) { return new Response("Missing key", { status: 400 }); } if (url.pathname === "/status") { return Response.json(await runAgent.getRunLifecycleStatus(fiberKey)); } if (url.pathname === "/result") { return Response.json(await runAgent.getRunTypedResult(fiberKey)); } return new Response("Not found", { status: 404 }); }, }; ``` This example has no authentication. `/start` bills a Nimble run per request. Add your own auth check before `wrangler deploy`, and give each authenticated principal its own agent instance name instead of the fixed `"research-session"`. See [Handling the API key](#handling-the-api-key). `startRun` accepts the work and returns before the create is attempted, so a missing `NIMBLE_API_KEY` still returns a successful-looking receipt. The failure appears on the next status read. Add `@cloudflare/workers-types` to your tsconfig `types`. ```bash theme={"system"} wrangler dev curl -s http://localhost:8787/start ``` `/start` returns a queued receipt with a `fiberKey`. Read `/status?key=...` a few times to watch the run progress, then `/result?key=...` once the status is terminal. Stay local until you have added an auth check. `wrangler dev` exercises the Durable Object, the ledger, and all three tools without exposing a billable endpoint. ```bash theme={"system"} wrangler deploy ``` Deploy once `/start` is behind your own authentication. See the warning in step 4. ### Example response `startRun` accepts the work and returns identity, not an answer: ```json theme={"system"} { "fiberKey": "nimble-run:generated:b0f1c7d4e2a98f6315c0d7b8a4e6f2913d5c8a70e1b4f9c2a6d3e8b1f704c295", "accepted": true, "runId": null, "status": "queued" } ``` `runId` is `null` on that first response because creation happens in the background. The Cloudflare Agents SDK calls that background task a fiber. It survives a restart. `accepted` is `true` when this call started a new run, and `false` when an identical request had already been submitted to this agent instance. On `false` the other fields describe the run that already exists. Check it before treating a response as a new run. Read the status to get the durable identifiers: ```bash theme={"system"} curl -s "https://my-research-worker.example.workers.dev/status?key=nimble-run:generated:b0f1c7d4..." ``` ```json theme={"system"} { "fiberKey": "nimble-run:generated:b0f1c7d4e2a98f6315c0d7b8a4e6f2913d5c8a70e1b4f9c2a6d3e8b1f704c295", "runId": "task_run_01k5m9v3x8zqhpc2n7t4wr6bde", "agentId": "wsa_01k5m9v3wq2fhtn8b4r7cxz1sd", "status": "running", "pollAttempts": 7, "recoveredCount": 0, "lastError": null } ``` Once the run is terminal, read the result: ```bash theme={"system"} curl -s "https://my-research-worker.example.workers.dev/result?key=nimble-run:generated:b0f1c7d4..." ``` ```json theme={"system"} { "fiberKey": "nimble-run:generated:b0f1c7d4e2a98f6315c0d7b8a4e6f2913d5c8a70e1b4f9c2a6d3e8b1f704c295", "runId": "task_run_01k5m9v3x8zqhpc2n7t4wr6bde", "agentId": "wsa_01k5m9v3wq2fhtn8b4r7cxz1sd", "status": "completed", "result": { "outputType": "text", "content": "National market surveillance authorities began issuing formal notices in 2026 [1] ...", "trust": { "confidence": "high", "reasoning": "Primary sources include the official EU register and two national authority notices.", "claims": [ { "confidence": "high", "reasoning": "Stated directly in the enforcement notice.", "callout": 1, "citations": [ { "url": "https://standards-authority.example.com/notices/2026-04", "title": "Market surveillance notice, April 2026", "excerpts": ["Enforcement powers apply from ..."] } ] } ], "sources": [ { "type": "primary", "url": "https://standards-authority.example.com/notices/2026-04", "title": "Market surveillance notice, April 2026" } ] }, "error": null }, "error": null } ``` ## How it works `getAgentByName(env.NIMBLE_RUN_AGENT, name)` returns a stub for one Durable Object. Every run started through that stub is recorded in that object's storage. The package hashes the full create request with SHA-256: the target agent ID plus `input`, `agent_name`, `effort`, `enable_events`, `input_data`, `output_schema`, `previous_interaction_id`, `skill`, `sources`, and `use_case`. The `fiberKey` is `nimble-run:`, then the agent ID or `generated`, then that hash. It is also the `startFiber()` idempotency key. Calling `startRun` twice with an identical request joins the existing run rather than billing a second one. Change `effort` and you get a different key, because Nimble bills that as different work. The create call reaches Nimble through `@nimble-way/nimble-js` configured with `maxRetries: 0`. The run ID and Nimble Agent ID are written to the durable ledger before polling starts. The fiber polls `GET /v2/agents/{agent_id}/runs/{run_id}` every 10 seconds by default. Safe reads retry with bounded exponential backoff and honor `Retry-After`. Say the Durable Object is evicted mid-run, or the Worker is redeployed. Recovery runs off a persisted alarm rather than waiting for an inbound request, and resumes polling from the stored run ID. It never re-issues a create. That alarm exists because a fiber is in flight. Once the fiber settles, including at the [polling deadline](#the-polling-deadline-is-local-not-terminal), nothing is left to recover and you read the run yourself. ### Run identity Three identifiers travel with a run. They are not interchangeable: | Identifier | Shape | What it is | | - | - | - | | `agentId` | `wsa_...` | The durable Nimble Agent. Reused across runs when you pass `nimbleAgentId`. | | `runId` | `task_run_...` | One research run against that agent. | | `fiberKey` | `nimble-run::<64 hex>` | The package's local handle for the run. Pass it to `status` and `result`. | Keep the `fiberKey` client-side or in your own store. It is the only argument the status and result tools need. ## The three tools Starts a research run and returns immediately. **Input** (`StartRunInput`): * `request` (required, `CreateRunRequest`): `input` is the task text and is required. Optional fields are `agent_name`, `effort`, `input_data`, `output_schema`, `previous_interaction_id`, `skill`, `sources`, `use_case`, and `enable_events`. * `nimbleAgentId` (optional): a `wsa_...` ID. Omit it and Nimble provisions the agent for the run. * `apiKeyOverride` (optional): an ephemeral per-request key that takes precedence over `env.NIMBLE_API_KEY`. It is never persisted. **Output** (`StartRunOutcome`): `fiberKey`, `accepted`, `runId`, `status`. `runId` is `null` until the background create lands. `accepted` is `false` when an identical request already exists on this instance. `use_case` is locked once the agent exists. A later run against the same agent must omit it or repeat its current value, or Nimble returns `422`. `skill`, `sources`, and `output_schema` stay overridable per run. `agent_name` applies only when `nimbleAgentId` is omitted, and `sources` is a `SourceGuidance` object, not a list of domains. **How create failures surface.** `startFiber` accepts work and returns without waiting for it, so `startRun` resolves before the create is attempted and none of these reach the caller as exceptions. Each one lands as `status: "failed"` with its message in `lastError` on the next `getRunLifecycleStatus`. Status is the error channel. * `GatedFeatureError` when `effort` is `max`. `max` is a custom-budget tier that is [coming soon](/nimble-sdk/web-search-agents/efforts#max). The package never silently substitutes `x-high`, so ask for `x-high` explicitly if you want it now. * `CreateRateLimitedError` on a definite `429`. Carries `retryAfterSeconds`. Never retried. * `AmbiguousCreateError` on a transport failure or a `5xx`, where the write may or may not have landed. Read [Duplicate protection](#duplicate-protection) before you do anything with this. * `NimbleAgentAPIError` for every other API failure. Carries `statusCode` and `refId`. Reads the durable ledger for one run. **Output** (`StatusOutcome`): `fiberKey`, `runId`, `agentId`, `status`, `pollAttempts`, `recoveredCount`, `lastError`. `status` is one of `queued`, `running`, `completed`, `failed`, or `cancelled`. Two more values come from the package itself: `unknown` when no ledger row exists, and `recovery-blocked` when a run created with `apiKeyOverride` lost that key across a restart. Anything else fails closed rather than being guessed at. A `recovery-blocked` row never resolves on its own. Background reconciliation skips it deliberately. Only `resumeWithOverride` clears it. This is a durable read, not a live call, so it is cheap. If the row looks stale, the package makes one safe `GET` to bring it current. It never issues a create. `apiKeyOverride` is only needed to reconcile a row that was started with an override key. Fetches the Nimble result once the run is terminal. **Output** (`ResultOutcome`): `fiberKey`, `runId`, `agentId`, `status`, `result`, `error`. `result` is `null` while the run is still active. Poll `getRunLifecycleStatus` until `status` is terminal, then call this. See [The result surface](#the-result-surface) for what `result` contains. If the run was started with `apiKeyOverride`, re-supply the same key here. The server fallback key is never substituted, and the result reads as `null` without it. Two recovery methods sit alongside the three tools. Neither is part of the model-facing surface: * `reconcileRunLifecycleStatus(fiberKey, apiKeyOverride?)` forces one safe `GET` regardless of the poll throttle, then returns the ledger. Use it for operator repair of a stuck row. * `resumeWithOverride(fiberKey, apiKeyOverride)` clears a `recovery-blocked` row once the caller re-supplies the key. It resumes an existing run and can never create one. ## The result surface `getRunTypedResult` normalizes the Nimble envelope into `NimbleResult` without flattening it: | Field | Type | Meaning | | - | - | - | | `outputType` | `"text"` \| `"json"` \| `"unknown"` | Which shape `content` holds. | | `content` | `string` \| object \| array \| `null` | Prose for text output, structured data for JSON output. `null` on a failed run. | | `trust` | `NimbleTrust` \| `null` | Confidence, reasoning, per-claim citations, and sources. `null` on a failed run. | | `error` | `{ message, refId? }` \| `null` | Structured failure detail. `null` on a completed run. | `trust.claims[]` carries a `confidence` grade, `reasoning`, and `citations[]` with `url`, `title`, and `excerpts`. Text output marks each claim with a `callout` number matching a marker in the prose. JSON output uses a `path` into the structure instead. `trust.sources[]` labels each source `primary` or `secondary`. Run identity travels with the result. `ResultOutcome` carries `runId` and `agentId` alongside `status` and `error`. A stored result stays traceable back to the run that produced it. See [Trust and citations](/nimble-sdk/web-search-agents/trust) for how the grades are assigned. ## Nimble research vs. Cloudflare AI Search Both return grounded answers. They ground them in different things. | Dimension | Nimble Agent | Cloudflare AI Search | | - | - | - | | Source | The live open web | Content you indexed into a Cloudflare data source | | Freshness | Read at run time | As fresh as your last index run | | Coverage | Anything publicly reachable | Only what you put in | | Shape | Multi-step research across many pages | Retrieval in a single request | | Latency | Seconds to minutes, by effort | Sub-second | | Output | Answer with per-claim trust grades and citations | Retrieved chunks, optionally generated over | When the answer is already in your corpus, AI Search is the better tool: faster, cheaper, and already wired into your account. Reach for a Nimble run when the answer sits on the public web and nobody indexed it for you. Competitor pricing this morning. A regulatory change last week. A company profile assembled from twenty sources. They compose. Index your own documents in AI Search, and commission a Nimble run when the corpus comes up short. ## Cost and latency Nimble bills per run, at a flat price per [effort level](/nimble-sdk/web-search-agents/efforts). Compute, retrieval, and storage are included. | Effort | Typical time | Price | | - | - | - | | `low` | 10 to 30 seconds | \$0.025 / run | | `medium` | 1 to 3 minutes | \$0.10 / run | | `high` | 5 to 15 minutes | \$0.50 / run | | `x-high` | 15 to 30 minutes | \$2.00 / run | `high` is the default for a run with no agent identity. A persistent Nimble Agent uses its own stored default, which templates set as high as `x-high` (\$2.00/run). Set `effort` explicitly if cost matters. `max` is a custom-budget tier and is coming soon; requesting it raises `GatedFeatureError`. Cloudflare charges separately for Durable Object requests, duration, and storage. A run parked between polls costs very little, but the ledger row persists until you remove it. See [Cleanup](#cleanup). ## Running this in production Everything below is what to get right once the example above works. ### Handling the API key `NIMBLE_API_KEY` is a Worker secret. It must never appear in: * model-facing tool schemas or tool inputs * browser or client state * run outputs, `lastError` strings, or fixtures * Worker logs, Gateway logs, screenshots, or evidence * code examples, including the ones in your own docs The tool inputs above carry a task and safe overrides. They carry no credential unless your application supplies `apiKeyOverride` server-side. That value is deliberately excluded from the durable ledger and from the fiber recovery snapshot. If a Worker serves more than one authenticated principal, route each trusted principal to a **separately named** `NimbleRunAgent` instance. Deduplication and the run ledger are scoped to one Durable Object instance. Multiplexing untrusted users into one instance lets them observe and join each other's runs. HTTP authentication is the consuming Worker's job. ### The asynchronous contract Creation returns identity, not an answer. Everything after that is a read. | Setting | Value | | - | - | | Default status poll interval | 10 seconds (`DEFAULT_POLL_INTERVAL_MS`) | | Minimum production poll interval | 10 seconds; shorter values are rejected | | Safe-read retry policy | 3 attempts, 500 ms base delay, 8 second cap (`DEFAULT_RETRY_POLICY`) | | `429 Retry-After` on a safe read | Honored, capped at the 8 second maximum delay | | `429` on a create | Fails immediately as `CreateRateLimitedError`, never retried | | Background polling budget | 5 minutes per fiber | Override the interval with the `NIMBLE_POLL_INTERVAL_MS` environment variable. Values below 10 seconds are rejected in production. Point the base URL elsewhere with `NIMBLE_BASE_URL`; it defaults to `https://sdk.nimbleway.com`. ### The polling deadline is local, not terminal Background polling stops after 5 minutes. A `high` run takes 5 to 15 minutes. An `x-high` run takes 15 to 30. A long run routinely outlives that budget. This is expected, and it is not a failure. When the deadline hits, the package records `"Polling deadline exceeded"` in `lastError` and **leaves `status` at the last real provider status**. It does not mark the run `failed`. The run is still going at Nimble. After the deadline no background poll is running. Poll `getRunLifecycleStatus` yourself. Each call at least one poll interval after the last update issues one safe `GET` and brings the row current. `reconcileRunLifecycleStatus` forces that read immediately. Read the deadline as "this Worker stopped watching", not "the run died". On a non-terminal row, `lastError: "Polling deadline exceeded"` means read status again later. It is not a reason to start a second run. ### Duplicate protection A Nimble run is billable, and running the same request twice bills twice. The package refuses to create one twice by accident: * **No automatic retry of creation.** Not on `429`, not on `5xx`, not on a transport failure. The underlying client runs with `maxRetries: 0`. * **Durable deduplication.** The `fiberKey` hash covers the target agent ID and the full create request. It is registered as the Agents SDK idempotency key, so an identical `startRun` joins the existing run and returns `accepted: false`. * **Ambiguous creates are never resubmitted.** A transport failure or a `5xx` gives no signal about whether the write landed. The package raises `AmbiguousCreateError` and stops. On `AmbiguousCreateError`, do not resubmit. Reconcile first. List the runs for the agent, or `GET` the run if an ID is already known. Only create a new run once you have established that none exists. Blind resubmission bills a second run and produces two answers to one question. Recovery follows the same rule. Say the Durable Object is evicted between the create and the confirmation. `onFiberRecovered()` marks the row rather than retrying. `lastError` then reads `Recovered before create was confirmed; not retried (ambiguous write)`. ### Scheduled and recurring runs The Agents SDK keeps fiber idempotency keys permanently, with no expiry and no automatic pruning. So a scheduled Worker that sends the same prompt to the same instance joins the first run every time. It returns the original answer, with no error and no second charge, and every status field reads healthy. Give each scheduled run its own Durable Object instance name. This is required for recurring work, not a tidiness measure. Keep passing the same `nimbleAgentId`. Agent memory and the recurring-cost reduction live on the Nimble side and are unaffected. Rotate only the Cloudflare-side instance name: ```ts theme={"system"} const today = new Date().toISOString().slice(0, 10); const runAgent = await getAgentByName(env.NIMBLE_RUN_AGENT, `monitor-${today}`); ``` The dedupe ledger is scoped per instance, so a fresh instance means a fresh run. Rotating also bounds ledger growth and pairs with `destroy()` once the result is stored. To keep one long-lived instance, you must prune the ledger yourself with `deleteFibers({ status: ["completed", "error", "aborted"], settledBefore })`. Nothing prunes it for you. ### Cleanup The run ledger lives in the Durable Object's SQLite storage and does not expire on its own. Two habits keep it bounded: * **Scope instance names.** Name the agent per session, per user, or per job rather than using one global instance forever. The ledger grows with the runs recorded in it. * **Destroy finished instances.** `Agent` exposes `destroy()`, inherited by `NimbleRunAgent`. Call it once every run in that instance is terminal and the results are stored somewhere durable. Treat it as fire-and-forget. * **Clear settled dedupe rows.** `deleteFibers({ status, settledBefore, limit })` removes terminal rows only. It defaults to 100 per call and caps at 500, so a busy instance needs repeated calls. Nothing sweeps them automatically. Do not destroy an instance with a non-terminal run in it. The run continues at Nimble and is billed either way. The local handle is gone, so the result has to be reconciled by run ID. ### Attribution Every request the package sends to Nimble carries `X-Client-Source: cloudflare-agents`. The package sets it. Do not set it yourself, and do not expect to see it in your own code. ## Optional: routing through AI Gateway [Cloudflare AI Gateway](https://developers.cloudflare.com/ai-gateway/) can sit in front of Nimble as a Custom Provider. It gives you Gateway analytics and logging over the same traffic. This is an optional path, not the recommended one. Agent API V2 is a multi-endpoint asynchronous lifecycle, not a chat-completions provider. The Gateway is a transparent HTTP hop, not a lifecycle-aware provider. It understands nothing about runs. The direct route is the default for good reason. Set the Custom Provider's own `base_url` to `https://sdk.nimbleway.com`, then point the package at the gateway: ```bash theme={"system"} NIMBLE_BASE_URL=https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/custom-{slug} ``` Everything after `custom-{slug}/` is appended, so `/v2/agents/...` routes correctly. **Set Retry requests to zero attempts.** A gateway-level retry of the create POST bills a second run, and the package's own `maxRetries: 0` cannot stop it. This is the setting that costs money if you get it wrong. **Leave Cache Responses off.** It is off by default. A cached status response reports a stale lifecycle state, and a cached create response hides whether a run was actually created. **Use an unauthenticated gateway.** The package sends a fixed header set and exposes no hook to add one, so `cf-aig-authorization` can never be sent. An authenticated gateway rejects every request. These are gateway-side settings, configured in the Cloudflare dashboard rather than in your Worker. Decide explicitly whether Gateway logs retain request and response bodies before you route research traffic through it. ## Using the Nimble MCP Server instead If you want Nimble tools in a Cloudflare agent without adding a dependency, connect the [Nimble MCP Server](/integrations/mcp-server/mcp-server) over Streamable HTTP. The Agents SDK connects to it directly: ```ts theme={"system"} import { Agent } from "agents"; interface Env { NIMBLE_API_KEY: string; } export class ResearchAgent extends Agent { async onStart() { await this.addMcpServer("nimble", "https://mcp.nimbleway.com/mcp", { transport: { type: "streamable-http", headers: { Authorization: `Bearer ${this.env.NIMBLE_API_KEY}`, }, }, }); } } ``` Pass the credential in the `Authorization` header, never in the URL. A key in a URL leaks into server access logs, browser history, and `Referer` headers sent to third parties. Header values are not logged the same way. This is the shortest path to Nimble tools in a Worker. It trades durability for setup speed: nothing here survives an eviction mid-run. The MCP server page covers the full transport options, the authentication options including OAuth, and the complete tool table. This section does not repeat them. [Code Mode](https://developers.cloudflare.com/agents/api-reference/code-mode/) is a third option: the model writes TypeScript against your tools instead of calling them one at a time. It works with either path above and is not documented here. ## Next steps What a research run is, and how to shape one. Effort levels, run times, and prices. How confidence grades and per-claim citations are produced. Transports, authentication, and the full tool table. Release notes and the published surface. Durable execution, fibers, and the Agents SDK. # Google ADK Source: https://docs.nimbleway.com/integrations/connectors/google-adk Connect Google ADK agents to the Nimble web data platform via the MCP server. Give Google ADK agents real-time web data by connecting to the Nimble MCP server. No extra SDK wiring needed. [Google ADK (Agent Development Kit)](https://google.github.io/adk-docs/) uses the [Model Context Protocol](https://modelcontextprotocol.io/) to connect agents to external tools. Since Nimble provides a hosted MCP server, ADK agents can discover and use all Nimble tools automatically. ## Prerequisites ```bash theme={"system"} pip install google-adk ``` Set environment variables: ```bash theme={"system"} export GOOGLE_API_KEY="your-google-api-key" export NIMBLE_API_KEY="your-nimble-api-key" ``` Get a Nimble API key from the [dashboard](https://online.nimbleway.com/settings/api-keys) (free trial available). The MCP examples pass an `X-Client-Source: google-adk` header, and the SDK example sets the Nimble client's `client_source` to `google-adk`, so requests are attributed to this integration. ## Quick Start Connect an ADK agent to the Nimble MCP server using `McpToolset` with `StreamableHTTPConnectionParams`. ADK auto-discovers all available Nimble tools: search, extraction, mapping, crawling, Extract Templates, and Nimble Agents. ```python Python theme={"system"} import os from google.adk.agents import LlmAgent from google.adk.tools.mcp_tool import McpToolset from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams NIMBLE_API_KEY = os.environ["NIMBLE_API_KEY"] root_agent = LlmAgent( model="gemini-2.5-flash", name="web_research_agent", instruction=( "You are a research assistant with access to real-time web data. " "Use the available tools to search the web, extract content from URLs, " "crawl sites, and discover URLs." ), tools=[ McpToolset( connection_params=StreamableHTTPConnectionParams( url="https://mcp.nimbleway.com/mcp", headers={"Authorization": f"Bearer {NIMBLE_API_KEY}", "X-Client-Source": "google-adk"} ) ) ], ) ``` ### Run the Agent ```python Python theme={"system"} import asyncio from google.adk.runners import Runner from google.adk.sessions import InMemorySessionService async def main(): session_service = InMemorySessionService() session = await session_service.create_session( app_name="nimble_app", user_id="user" ) runner = Runner( agent=root_agent, app_name="nimble_app", session_service=session_service, ) from google.genai import types response = await runner.run_async( user_id="user", session_id=session.id, new_message=types.Content( role="user", parts=[types.Part(text="What are the latest trends in AI agents?")] ), ) for event in response: if event.content and event.content.parts: for part in event.content.parts: if part.text: print(part.text) asyncio.run(main()) ``` ## Filter Tools By default, ADK discovers all Nimble MCP tools. Use `tool_filter` to expose only the tools the agent needs: ```python Python theme={"system"} McpToolset( connection_params=StreamableHTTPConnectionParams( url="https://mcp.nimbleway.com/mcp", headers={"Authorization": f"Bearer {NIMBLE_API_KEY}", "X-Client-Source": "google-adk"} ), tool_filter=["nimble_search", "nimble_extract"] ) ``` ## Agent API V2: autonomous research tool The hosted MCP tools above (`nimble_search`, `nimble_extract`, `nimble_map`, `nimble_crawl_run`, and `nimble_extract_templates_run`) are **synchronous and single-shot**. One call returns data directly, and the ADK model does the reasoning. [Agent API V2](/nimble-sdk/web-search-agents/overview) is different. It exposes an **asynchronous research agent** that plans, searches across many sources, and returns a synthesized answer with a per-claim [trust report](/nimble-sdk/web-search-agents/trust). The lifecycle is **create a run → poll to a terminal state → retrieve the result**. The Nimble MCP server exposes Agent API V2 too (creating a run, polling its status, and fetching the result), so an MCP-connected ADK agent can call these tools directly. Because a V2 run is asynchronous, you can also wrap the [Nimble SDK](/nimble-sdk/sdks/python) lifecycle in a single Python function. The model then calls one synchronous "deep research" tool, and the create-poll-retrieve loop stays in your code. Register it as an ADK callable tool alongside the MCP toolset shown above. ```bash theme={"system"} pip install google-adk nimble_python ``` ### 1. Wrap the run lifecycle This helper uses a stable `agent_name`, so every call — from this process or any other — reuses the same agent and its memory instead of spinning up a fresh one each time. It creates the run, polls until terminal, handles `failed` and `cancelled` runs, and returns the answer plus its trust and citation metadata. Errors return a safe message, so no API key or raw exception is surfaced. ```python Python theme={"system"} import os import time from nimble_python import Nimble nimble_client = Nimble(api_key=os.environ["NIMBLE_API_KEY"], client_source="google-adk") # Stable name for this integration's research agent. The first call creates # it; every later call (here or from another process) reuses it and its memory. # Pick a name unique to this deployment - anyone reusing the same name on the # same account reuses the same agent and its memory. NIMBLE_AGENT_NAME = os.environ["NIMBLE_AGENT_NAME"] def nimble_deep_research(query: str) -> dict: """Run an autonomous, fully-cited web-research task with Nimble Agent API V2. Use for open-ended questions that need synthesis across multiple sources, not single-page lookups. Returns a cited answer with a trust grade. Args: query: The research question or task, in plain language. Returns: A dict with the answer, an overall confidence grade, and citations. """ try: # Create-or-reuse the named agent, then start the run. run = nimble_client.agents.run(input=query, agent_name=NIMBLE_AGENT_NAME) agent_id = run.web_search_agent_id # Poll until the run reaches a terminal state. deadline = time.time() + 300 while run.is_active: if time.time() > deadline: return {"status": "timeout", "error": "Run did not finish in time."} time.sleep(10) run = nimble_client.agents.runs.get(run.id, agent_id=agent_id) # Handle failed or cancelled runs. if run.status != "completed": return {"status": run.status, "error": f"Run ended as '{run.status}'."} # Retrieve the completed result and summarize its trust report. result = nimble_client.agents.runs.result(run.id, agent_id=agent_id) output = result.output return { "status": "completed", "answer": output.content, "confidence": output.trust.confidence, "citations": [ {"url": s.url, "title": s.title, "source_type": s.type} for s in output.trust.sources ], } except Exception: # Never leak credentials or raw exceptions back to the model. return {"status": "error", "error": "Nimble request failed."} ``` Pass an `output_schema` to `agents.run` (or `agents.runs.create`) to get structured JSON in `result.output` instead of prose. Trust is then keyed by JSON path. See [Dataset Building](/nimble-sdk/web-search-agents/use-cases/dataset-building). ### 2. Register it as an ADK tool Pass the function straight into `LlmAgent`. ADK auto-wraps it as a `FunctionTool`, reading the signature and docstring for the schema. The model calls one tool; the async run loop stays hidden. ```python Python theme={"system"} from google.adk.agents import LlmAgent research_agent = LlmAgent( model="gemini-2.5-flash", name="deep_research_agent", instruction=( "You are a research analyst. Use nimble_deep_research for questions " "that need current, cited web evidence. Report the confidence grade " "and cite the sources it returns." ), tools=[nimble_deep_research], ) ``` Run it with the same `Runner` setup shown in the [Quick Start](#run-the-agent). The tool returns the synthesized answer, an overall `confidence` grade (`high`, `medium`, or `low`), and the source URLs behind it, so the model can weigh how much to trust each claim. See [Trust](/nimble-sdk/web-search-agents/trust) for the full report structure. ## Available Tools ADK auto-discovers these tools from the Nimble MCP server: | Tool | Description | | - | - | | `nimble_search` | Web search with configurable depth and focus modes | | `nimble_extract` | Extract content from any URL with rendering support | | `nimble_map` | Discover all URLs on a website via sitemaps and link crawling | | `nimble_crawl_run` | Crawl multiple pages with path filtering and progress tracking | | `nimble_extract_templates_run` | Run pre-built [Extract Templates](/nimble-sdk/web-tools/extract/template) for structured data from popular sites | The tool names above are a curated subset. See the [Nimble MCP Server](/integrations/mcp-server/mcp-server) docs for setup details and the full tool list. ## Next Steps Full MCP server setup for Cursor, Claude Desktop, and other clients Agent API V2: autonomous research runs with per-claim trust Use Nimble with OpenAI function calling and the Agents SDK Use Nimble with Claude's tool-use API and Tool Runner # Hermes Source: https://docs.nimbleway.com/integrations/connectors/hermes Run durable, cited Nimble Agent API V2 research runs from inside the Hermes agent using the standalone hermes-nimble-agent plugin. The [`hermes-nimble-agent`](https://pypi.org/project/hermes-nimble-agent/) plugin connects [Hermes](https://hermes-agent.nousresearch.com/) to Nimble [Agent API V2](/nimble-sdk/web-search-agents/overview). It lets a Hermes user start a research run, walk away, and retrieve a cited answer later from any session. Agent API V2 is not a single search or extract call. It runs a stateful, asynchronous research workflow on Nimble's infrastructure. The plugin returns durable identifiers the moment a run starts, then the research continues after Hermes returns. Any later Hermes session can resume the run using those identifiers. The standalone plugin is the recommended path. A [production MCP fallback](#production-mcp-fallback) exists for clients that cannot install the plugin, but it does not offer full parity. ## Overview The plugin adds five tools and one skill to Hermes: * **Five tools** cover the run lifecycle: discover an agent, create one from a template if needed, start a run, check status, and fetch the result. * **One skill**, `nimble-agent:agent-research`, teaches Hermes the reuse-first, poll-later workflow so runs are handled correctly without manual prompting. Every run returns an answer with a trust block: overall confidence, per-claim citations, and verbatim source excerpts. That trust model is the reason to use Agent API V2 instead of a raw search result. The workflow is asynchronous by design: 1. Start a run. The plugin returns `agent_id` and `run_id` immediately and never blocks. 2. The research continues on Nimble's infrastructure. 3. Check status later, from the same session or a new one, using the two identifiers. 4. Fetch the terminal result once the run completes. ## Prerequisites The plugin is built and tested against `hermes-agent==0.19.0`. Install it into the **same Python environment** as `hermes-agent`. Supported on Python 3.11, 3.12, and 3.13. The plugin depends on `nimble-python>=1.0.0,<2`, which pip installs automatically. Create a Nimble account and generate an API key from [Settings → API Keys](https://online.nimbleway.com/settings/api-keys). A free trial is available. No extra subscription, quota, or availability tier is required beyond a valid key. ## Installation Install the plugin, then enable it in Hermes. ```bash theme={"system"} pip install hermes-nimble-agent==0.1.0 # same environment as hermes-agent hermes plugins enable nimble-agent ``` The plugin slug is `nimble-agent`. Enabling adds it to the Hermes allow-list. Plugins are opt-in and stay disabled until enabled. Enabling takes effect on the next session. Start a new Hermes session before the tools become visible. Verify that the plugin and its five tools loaded: ```bash theme={"system"} hermes plugins list ``` If the tools do not appear, run `HERMES_PLUGINS_DEBUG=1 hermes plugins list` to see discovery details. ## Authentication The plugin reads your key from the `NIMBLE_API_KEY` environment variable. It is never passed as a tool argument. ```bash theme={"system"} export NIMBLE_API_KEY="your-nimble-api-key" ``` Use a placeholder or an environment variable. Never commit a real key or paste one into a prompt or config file. Two behaviors protect the key: * **Tools stay hidden when the key is missing.** All five tools are gated on `NIMBLE_API_KEY`. If it is absent or empty, Hermes does not show them to the model. Set the key and start a new session to reveal them. * **The key is scrubbed from errors.** The plugin never stores, logs, or echoes the key, and replaces it with `[REDACTED]` in any error text before returning it. The plugin also tags every request with the header `X-Client-Source: hermes` automatically. Nothing to configure. ## Available tools The toolset name is `nimble_agent`. Each tool returns a JSON string and never raises. | Tool | Purpose | Required inputs | Optional inputs | | - | - | - | - | | `nimble_agents_list` | List account agents. Discover an existing agent to reuse. | none | `limit` (1-50), `offset` | | `nimble_agent_bootstrap` | List prebuilt templates, or create a persistent agent from one. | none | `template`, `display_name` | | `nimble_agent_run_start` | Start an async run. Returns identifiers immediately. | `agent_id`, `task` | `effort` | | `nimble_agent_run_status` | Check run status using the identifiers alone. | `agent_id`, `run_id` | none | | `nimble_agent_run_result` | Fetch the terminal answer and trust metadata. | `agent_id`, `run_id` | none | How they connect: * `nimble_agents_list` and `nimble_agent_bootstrap` produce an `agent_id` (`wsa_...`). Reuse an existing agent before creating a new one. * `nimble_agent_run_start` takes that `agent_id` plus a `task` (the research question) and returns a `run_id` (`task_run_...`). * `nimble_agent_run_status` and `nimble_agent_run_result` take both identifiers. They work from any session, so the two identifiers are all a later session needs. ### The agent-research skill The plugin bundles a skill named `nimble-agent:agent-research`. It teaches the full lifecycle: reuse before create, return identifiers within the turn, avoid tight polling, resume from identifiers alone, handle `not_ready` and failed runs, and present answers with citations and confidence. Plugin skills are opt-in and do not appear in the system-prompt skill index. Load it explicitly when you want Hermes to follow the workflow: ```text theme={"system"} skill_view('nimble-agent:agent-research') ``` ## Complete asynchronous workflow This example uses placeholder identifiers and a generic research question. Identifier values shown are illustrative. List existing agents and reuse one when possible: ```text theme={"system"} nimble_agents_list() ``` If none fits, list templates and create an agent from one: ```text theme={"system"} nimble_agent_bootstrap() # lists available templates nimble_agent_bootstrap(template="") # creates an agent from a listed template ``` Use a `template` value from the list the first call returns. Both paths yield an `agent_id` such as `wsa_xxxxxxxxxxxx`. Start a medium-effort run. `effort` is optional; omit it to use the agent's default. ```text theme={"system"} nimble_agent_run_start( agent_id="wsa_xxxxxxxxxxxx", task="Summarize the main approaches to retrieval-augmented generation and cite sources.", effort="medium" ) ``` The response returns immediately: ```json theme={"system"} { "run": { "run_id": "task_run_xxxxxxxxxxxx", "agent_id": "wsa_xxxxxxxxxxxx", "status": "queued", "effort": "medium", "is_active": true }, "agent_id": "wsa_xxxxxxxxxxxx", "run_id": "task_run_xxxxxxxxxxxx", "next": "Save both identifiers. Check status later with nimble_agent_run_status." } ``` Record `agent_id` and `run_id`. They are the only state needed to resume. You can now end the session or move on to other work. From the same session or a new one: ```text theme={"system"} nimble_agent_run_status(agent_id="wsa_xxxxxxxxxxxx", run_id="task_run_xxxxxxxxxxxx") ``` `status` is `queued` or `running` while active, and `completed`, `failed`, or `cancelled` when terminal. Once the run is `completed`: ```text theme={"system"} nimble_agent_run_result(agent_id="wsa_xxxxxxxxxxxx", run_id="task_run_xxxxxxxxxxxx") ``` The result carries the answer and its trust block. Present the answer, then list the sources and per-claim citations. Medium and higher efforts can take several minutes. Return the identifiers to the user rather than holding a tool call open, and space status checks tens of seconds apart. ## Result and trust model `nimble_agent_run_result` returns the answer wrapped in the plugin's result envelope: ```json theme={"system"} { "success": true, "output": { "type": "text", "content": "Retrieval-augmented generation combines...", "trust": { "confidence": "high", "reasoning": "...", "sources": [ ... ], "claims": [ ... ] } }, "sources_count": 7, "claims_count": 7, "agent_id": "wsa_xxxxxxxxxxxx", "run_id": "task_run_xxxxxxxxxxxx" } ``` Envelope specifics: * **`output.type`** is `text` for a prose answer, or `json` when the agent is configured with an output schema. Structured runs return `content` as an object or array. * **`sources_count`** and **`claims_count`** are always present integers. The `trust` block (overall confidence, per-claim citations, primary and secondary source classification, and verbatim excerpts) follows the standard Agent API V2 trust model. See [Trust](/nimble-sdk/web-search-agents/trust) for how grades and citations work, and the [run result schema](/api-reference/public-api/get-agent-run-result) for every field. The trust block is present but its detail varies with effort. A `low`-effort run may consult few or no live sources, in which case it honestly reports low confidence with zero sources. Use `medium` or higher when citations matter. ## Errors and recovery Every failure returns a structured envelope: `{ "success": false, "error_type": "...", "error": "...", "retriable": true|false }`. The `agent_id` and `run_id` are preserved even on validation errors, so a run is never lost to a client-side error. | `error_type` | Meaning | Action | Retriable | | - | - | - | - | | `not_ready` | Result requested before the run finished. | Check status, then retry the result later. | Yes | | `terminal_failure` | Run failed or was cancelled. | Read the server message. Start a new run if appropriate. | No | | `auth` | API key missing or invalid. | Set a valid `NIMBLE_API_KEY`. | No | | `permission` | Key lacks access to the resource. | Check account permissions. | No | | `not_found` | Unknown `agent_id` or `run_id`. | Confirm the identifiers. | No | | `validation` | Bad or missing arguments. | Fix the arguments and retry. | No | | `rate_limited` | Too many requests. | Back off, then retry. See [Rate limits](/nimble-sdk/admin/rate-limits). | Yes | | `timeout` | Request timed out in transit. | Retry. The run may still be active. | Yes | | `connection` | Network error reaching Nimble. | Retry. The run may still be active. | Yes | | `dependency` | `nimble-python` is not installed. | Install it in the Hermes environment. | No | | `protocol` | Unexpected API response shape. | Retry status or result. Report if it persists. | No | A transport error such as `timeout` or `connection` does not necessarily end the server-side run. The run keeps going on Nimble's infrastructure. Check status again with the same identifiers before starting over. Poll considerately. Space status checks tens of seconds apart rather than in a tight loop. ## Effort and runtime guidance `nimble_agent_run_start` accepts an optional `effort` (`low`, `medium`, `high`, `x-high`, `max`). Omit it to use the agent's default. Higher effort reads more sources and takes longer, from seconds at `low` to many minutes at higher tiers. See [Efforts](/nimble-sdk/web-search-agents/efforts) for the full tier list, typical run times, pricing, and the default. The bundled skill suggests `medium` or higher when citations matter. Because a run can take minutes, the plugin returns `agent_id` and `run_id` immediately rather than holding a tool call open. Let the model report the identifiers, then check status later and space checks tens of seconds apart. ## Plugin or MCP? | Consideration | Standalone plugin | Production MCP fallback | | - | - | - | | Recommended status | Primary | Secondary fallback | | Installation | PyPI plus `hermes plugins enable` | `~/.hermes/config.yaml` MCP server entry | | Run lifecycle | Five dedicated tools (list, bootstrap, start, status, result) | Generic Nimble agent MCP tools | | Identity requirement | Requires `agent_id` | Requires `agent_id` | | Resumable runs | Yes, from identifiers alone | Yes, same durable identifiers | | Trust and citations | Full trust block mapped for you | Raw API result; presentation is up to you | | Attribution | `X-Client-Source: hermes` sent automatically | Set the header manually in config | | Tool filtering | Fixed five-tool toolset | Allowlist you define | | Credential redaction | Built in | Not provided | | Bundled skill | `nimble-agent:agent-research` included | Not included | The plugin is the recommended path. Generic MCP connectivity to Nimble does not by itself provide the plugin's lifecycle handling, trust mapping, credential redaction, or bundled skill. ## Production MCP fallback Use this path only when installing the plugin is not possible. It connects Hermes to the production Nimble MCP server over Streamable HTTP. Treat the block below as a starting configuration and validate it against your Hermes and Nimble MCP versions before relying on it. Add the server to `~/.hermes/config.yaml`: ```yaml theme={"system"} mcp_servers: nimble: url: "https://mcp.nimbleway.com/mcp" headers: Authorization: "Bearer ${NIMBLE_API_KEY}" X-Client-Source: "hermes" timeout: 60 tools: include: - nimble_agents_list - nimble_agents_run - nimble_agents_run_status - nimble_agents_run_result ``` Notes: * **Authentication** uses a bearer token. Keep the key in the `NIMBLE_API_KEY` environment variable and reference it with `${NIMBLE_API_KEY}`. * **Attribution** over MCP is manual. Add `X-Client-Source: hermes` to the headers as shown. It is not sent automatically the way the plugin sends it. * **The tool allowlist** limits Hermes to the agent run lifecycle. Confirm the exact tool names against your server's advertised tool list, since a name that does not match is silently filtered out, and adjust the list to your needs. * **Timeouts** should allow for research that runs for minutes. Runs continue server-side even if a request times out. * Reload the server mid-session with `/reload-mcp`, or restart Hermes. The MCP fallback requires an explicit `agent_id`, the same as the plugin. It does not provide the plugin's trust mapping, credential redaction, or bundled skill, and is not a full-parity replacement. See the [Nimble MCP Server](/integrations/mcp-server/mcp-server) docs for the full server reference. ## Troubleshooting | Symptom | Cause | Action | | - | - | - | | Plugin does not appear | Not enabled | Run `hermes plugins enable nimble-agent`, then start a new session. | | Tools are hidden | `NIMBLE_API_KEY` missing | Set the key, then start a new session. | | Enabled but no tools this session | Enabling applies to the next session | Start a new Hermes session. | | Wrong Python environment | Plugin not in the `hermes-agent` environment | Reinstall in the same environment. Confirm with `hermes plugins list`. | | `auth` error | Key missing or invalid | Set a valid `NIMBLE_API_KEY`. | | Run stays `queued` or `running` | Research still in progress | Wait and check status again. Higher effort takes longer. | | Lost identifiers | `agent_id` or `run_id` not saved | Use `nimble_agents_list` to find the agent. Preserve identifiers next time. | | Result says `not_ready` | Run not finished | Check status, then retry the result. | | `rate_limited` | Too many requests | Back off, then retry. | | MCP auth or timeout failure | Bad header or short timeout | Check the `Authorization` header and raise `timeout`. | ## Related resources Install `hermes-nimble-agent` from public PyPI. Source, README, and release notes. Learn how Nimble Agent API V2 research runs work. How confidence, sources, and per-claim citations are produced. Full reference for the production MCP fallback. How Hermes discovers and enables plugins. # LangChain Source: https://docs.nimbleway.com/integrations/connectors/langchain LangChain tools and retrievers for web search, content extraction, Extract Templates, Map/Crawl, and resumable Agent API V2 research. ### Overview The [`langchain-nimble`](https://pypi.org/project/langchain-nimble/) package provides production-grade LangChain integrations for the Nimble web data platform. Built on the official [`nimble_python`](https://pypi.org/project/nimble_python/) SDK, it enables RAG applications and AI agents that can search, extract, scrape with templates, crawl, map, and run resumable Nimble Agent research. **NimbleToolkit** is the single entry point. Enable the tool families you need with include flags. **Choose the right capability** (these are not interchangeable): | Need | Use | Public tool names | | - | - | - | | Ranked web results for RAG or agents | Search | `nimble_search` / `NimbleSearchRetriever` | | Clean markdown/HTML from a known URL | Extract | `nimble_extract` / `NimbleExtractRetriever` | | Structured scrape of known page types (PDP, jobs, …) | [Extract Templates](/nimble-sdk/web-tools/extract/template) | `nimble_extract_template_*` | | Multi-page crawl or URL discovery | Crawl / Map | `nimble_crawl`, `nimble_map` | | Deep research with citations and trust | [Agents API](/nimble-sdk/web-search-agents/overview) | `nimble_web_search_agent_*` | Do not treat Extract Templates and Agent API V2 as the same product. Legacy `nimble_agent_*` names are **deprecated aliases for Extract Templates only** — they are not repointed to Agent API V2 research agents. #### Key Features * **Search depths** — `lite`, `fast`, `deep`, plus focus modes and domain/date filters * **Extract Templates** — list → get → run structured site scrapers * **Agent API V2** — separate **start / status / result** tools (resumable; typically 3–15 minutes) * **Unified toolkit** — one API key; opt into templates, agents, crawl, and map * **Full async** — sync and async via `nimble_python` * **Attribution** — every request sends `X-Client-Source: langchain-nimble` ### Requirements * Python 3.10+ * `langchain-nimble` **4.0.0+** (depends on `nimble_python>=1.2.0,<2.0.0`) ### Quick Start #### Installation ```bash theme={"system"} pip install -U langchain "langchain-nimble>=4.0.0" langchain-openai ``` #### Setup Get your API key from [Nimble's dashboard](https://online.nimbleway.com/account-settings/api-keys) (free trial available): ```bash theme={"system"} export NIMBLE_API_KEY="your-api-key" ``` You can also pass `api_key=` into tools, retrievers, or `NimbleToolkit`. #### Build an AI Agent with the Toolkit ```python theme={"system"} from langchain.agents import create_agent from langchain_nimble import NimbleToolkit from langchain_openai import ChatOpenAI # Search + Extract on by default; opt into other families as needed toolkit = NimbleToolkit( include_crawl=True, include_map=True, include_extract_templates=True, include_web_search_agents=True, ) tools = toolkit.get_tools() agent = create_agent( model=ChatOpenAI(model="gpt-5"), tools=tools, system_prompt=( "You are a research assistant with Nimble web tools.\n" "Use search/extract for quick lookups, extract templates for " "structured scrapes, and Agent API V2 (start → status → result) " "for deep research that may take several minutes." ), ) response = agent.invoke({ "messages": [( "user", "What are the latest developments in AI agents? Summarize key findings.", )] }) print(response["messages"][-1].content) ``` ### Toolkit Flags | Flag | Default | Tools | | - | - | - | | `include_search` | `True` | `nimble_search` | | `include_extract` | `True` | `nimble_extract` | | `include_crawl` | `False` | `nimble_crawl` | | `include_map` | `False` | `nimble_map` | | `include_extract_templates` | `False` | `nimble_extract_template_list` / `_get` / `_run` | | `include_web_search_agents` | `False` | `nimble_web_search_agents_list`, templates list, create, `run_start` / `run_status` / `run_result` | | `include_agent` | `False` | **Deprecated** — adds `nimble_agent_*` Extract Template aliases and emits `DeprecationWarning`. Prefer `include_extract_templates`. | You can enable `include_agent` and `include_web_search_agents` together; both families are returned (aliases + V2). ### Extract Templates For structured scraping of known page types (product pages, listings, and similar), use Extract Templates — not Agent API V2. ```python theme={"system"} from langchain_nimble import NimbleToolkit toolkit = NimbleToolkit( include_search=False, include_extract=False, include_extract_templates=True, ) tools = toolkit.get_tools() # nimble_extract_template_list → get → run(template=..., params={...}) ``` Typical agent workflow: 1. `nimble_extract_template_list` — discover templates for the account 2. `nimble_extract_template_get` — inspect schema / published version 3. `nimble_extract_template_run` — run with `template` + `params` See the [Extract Templates guide](/nimble-sdk/web-tools/extract/template) and [API reference](/api-reference/extract-templates-api/run-extract-template). ### Agents API Deep research / enrichment / dataset building via the [Agents API](/nimble-sdk/web-search-agents/overview). Tools are **resumable**: `run_start` returns immediately with identifiers; poll `run_status`, then fetch `run_result`. Do not expect a multi-minute research job to finish inside one tool call. Agent **`use_case`** (`research`, `enrichment`, or `dataset_building`) is set when the agent is created and locked afterward — that is separate from how you bootstrap a run below. ```python theme={"system"} from langchain_nimble import NimbleToolkit toolkit = NimbleToolkit( include_search=False, include_extract=False, include_web_search_agents=True, ) tools = toolkit.get_tools() ``` | Tool | Role | | - | - | | `nimble_web_search_agents_list` | List existing agents (`wsa_…`) | | `nimble_web_search_agent_templates_list` | List agent templates | | `nimble_web_search_agent_create` | Create a persistent agent (for `agent_id` runs) | | `nimble_web_search_agent_run_start` | Start a run (`agent_name`, `agent_id`, or anonymous) | | `nimble_web_search_agent_run_status` | Poll status | | `nimble_web_search_agent_run_result` | Fetch output + trust / citations | #### Bootstrap Options How you identify the agent on `run_start` (distinct from `use_case`): | Option | When | How | | - | - | - | | **`agent_name` (default for LangChain)** | Stateless tool sessions | `run_start` with `agent_name` + optional `use_case` / `skill` / `sources` — create-or-reuse | | **`agent_id`** | You persist `wsa_…` | Create (or list) an agent, then `run_start` with `agent_id` | | **Anonymous** | One-off demos | Omit both `agent_id` and `agent_name` | Map response fields carefully for status/result tools: * Start response `id` → **run id** (`task_run_…`) * Start response `web_search_agent_id` → **agent id** (`wsa_…`) #### Effort, Use Cases, and Overrides * **Effort:** `low` | `medium` | `high` | `x-high` | `max`. Plan for **3–15 minutes** on real research (not a few seconds). * **`use_case`:** `research` (text), `enrichment` (JSON + `input_data`), `dataset_building` (JSON). Locked after agent create — a different value on reuse returns **422**. * **Run-level overrides** (`skill`, `sources`, `output_schema`) do not mutate the stored agent, except the **first** `agent_name` create with a new name. * **`input_data`** is enrichment payload only (never stored on the agent); distinct from `output_schema`. ```python theme={"system"} from langchain_nimble import NimbleAgentRunStartTool, NimbleAgentRunStatusTool, NimbleAgentRunResultTool start = NimbleAgentRunStartTool() started = start.invoke({ "input": "Summarize recent Agent API V2 changes for integrators.", "agent_name": "langchain_docs_research", "use_case": "research", "effort": "medium", "skill": "Focus on integrator-facing API behavior.", }) agent_id = started["web_search_agent_id"] run_id = started["id"] status = NimbleAgentRunStatusTool().invoke({ "agent_id": agent_id, "run_id": run_id, }) # Poll until status is completed / failed / cancelled, then: result = NimbleAgentRunResultTool().invoke({ "agent_id": agent_id, "run_id": run_id, }) ``` Runnable examples in the package repo: [`examples/agent_api_v2.py`](https://github.com/Nimbleway/langchain-nimble/blob/main/examples/agent_api_v2.py) and [`examples/web_search_agent.py`](https://github.com/Nimbleway/langchain-nimble/blob/main/examples/web_search_agent.py) (templates + search/extract/map/crawl). ### Crawl and Map ```python theme={"system"} from langchain_nimble import NimbleCrawlTool, NimbleMapTool crawl_tool = NimbleCrawlTool() result = crawl_tool.invoke({"url": "https://docs.example.com", "max_pages": 50}) map_tool = NimbleMapTool() urls = map_tool.invoke({"url": "https://www.example.com"}) ``` Crawl polls internally until the job finishes (or times out). Map discovers URLs via sitemap + link crawling. ### Retrievers (RAG) ```python theme={"system"} from langchain_nimble import NimbleSearchRetriever, NimbleExtractRetriever search = NimbleSearchRetriever(max_results=5, search_depth="lite") docs = search.invoke("latest developments in AI") # Extract: pass the URL as the retriever query extract = NimbleExtractRetriever(output_format="markdown") pages = extract.invoke( "https://docs.langchain.com/oss/python/integrations/providers/overview" ) ``` ### Deprecated Aliases (4.0.0) | Deprecated | Prefer | | - | - | | `include_agent=True` | `include_extract_templates=True` | | `nimble_agent_list` / `_get` / `_run` | `nimble_extract_template_*` | | `include_agents` | `include_web_search_agents` | Breaking notes and SDK floor: see the package [CHANGELOG 4.0.0](https://github.com/Nimbleway/langchain-nimble/blob/main/CHANGELOG.md). ### Attribution The package sets SDK `client_source="langchain-nimble"`, which sends: ```http theme={"system"} X-Client-Source: langchain-nimble ``` No extra configuration is required. ### Additional Resources * [GitHub repository](https://github.com/Nimbleway/langchain-nimble) * [PyPI package](https://pypi.org/project/langchain-nimble/) * [Agents API overview](/nimble-sdk/web-search-agents/overview) * [Extract Templates](/nimble-sdk/web-tools/extract/template) * [Nimble Python SDK](/nimble-sdk/sdks/python) * [Example cookbook](https://github.com/Nimbleway/cookbook) * [LangChain integrations overview](https://docs.langchain.com/oss/python/integrations/providers/overview) # LiteLLM Source: https://docs.nimbleway.com/integrations/connectors/litellm Nimble is a native search provider in LiteLLM: reach Nimble Search from the LiteLLM Python SDK or the AI Gateway with a single parameter. ### Overview [LiteLLM](https://github.com/BerriAI/litellm) is an SDK and AI gateway that gives 100+ LLM providers one OpenAI-shaped interface. Its Search API does the same for web search backends. Nimble ships as the `nimble` search provider, built into LiteLLM itself. Calls go to Nimble's [Search API](/nimble-sdk/web-tools/search) and come back in LiteLLM's unified search response. No fork, no plugin, no custom adapter. The provider ships in **LiteLLM v1.98.0 and later**. Pin that floor rather than installing "latest": ```bash theme={"system"} pip install "litellm>=1.98.0" ``` `v1.97.0` shipped without the provider. An existing install that predates `v1.98.0` needs an upgrade before `search_provider="nimble"` resolves. ### What this integration covers This is the Search API only. `search_provider: nimble` routes to `POST /v2/search` and nothing else. Extract, Map, Crawl, and Nimble Agents are not reachable through LiteLLM today. To use those, call the [Nimble SDK](/nimble-sdk/getting-started/overview) directly or connect the [Nimble MCP server](/integrations/mcp-server/mcp-server). ### Quick Start #### 1. Get a Nimble API key Get your API key from [Nimble's dashboard](https://online.nimbleway.com/settings/api-keys) (free trial available) and export it: ```bash theme={"system"} export NIMBLE_API_KEY="your-api-key" ``` #### 2. Search from the Python SDK Call `search()` with `search_provider="nimble"`: ```python theme={"system"} import os from litellm import search os.environ["NIMBLE_API_KEY"] = "your-api-key" response = search( query="latest AI developments", search_provider="nimble", max_results=5, ) for result in response.results: print(f"{result.title}: {result.url}") print(result.snippet) ``` `search_provider` is required and has no default. LiteLLM does not pick a search backend for you, so installing the package is not enough to reach Nimble. An async `asearch()` with the same signature is also available. #### 3. Or search through the AI Gateway Register Nimble as a search tool in `config.yaml`: ```yaml theme={"system"} search_tools: - search_tool_name: nimble-search litellm_params: search_provider: nimble api_key: os.environ/NIMBLE_API_KEY ``` Start the gateway: ```bash theme={"system"} litellm --config /path/to/config.yaml # RUNNING on http://0.0.0.0:4000 ``` Then call the search endpoint. The path segment is the `search_tool_name` you registered: ```bash theme={"system"} curl http://0.0.0.0:4000/v1/search/nimble-search \ -H "Authorization: Bearer sk-1234" \ -H "Content-Type: application/json" \ -d '{ "query": "latest AI developments", "max_results": 5 }' ``` Send your LiteLLM virtual key in the `Authorization` header, not your Nimble key. The gateway holds the Nimble credential server-side, so callers never see it. ### Credentials **Required.** Your Nimble API key. The provider reads it from the environment, or you can pass `api_key=` per call in the SDK and `api_key:` per search tool in the gateway config. Without it, the provider raises `NIMBLE_API_KEY is not set` before any request leaves your machine. **Optional.** Overrides the API base. Defaults to `https://sdk.nimbleway.com/v2`, and the provider appends `/search` itself. Set it only to point at a proxy or a regional endpoint. ### Parameters LiteLLM maps four unified parameters onto Nimble's names, then forwards every other parameter to the Search API untouched. | LiteLLM parameter | Sent to Nimble as | | - | - | | `query` | `query`. A list is joined with spaces, since Nimble takes one string. | | `max_results` | `max_results`, unclamped. Nimble validates the 1 to 100 range and reports the error. | | `country` | `country`, upper-cased to the ISO-2 form Nimble expects. | | `search_domain_filter` | `include_domains`, with `-`-prefixed hosts going to `exclude_domains`. | | `max_tokens_per_page` | Dropped. Nimble has no equivalent. | Passing Nimble's own `include_domains` or `exclude_domains` overrides anything derived from `search_domain_filter`. Anything LiteLLM does not recognize goes into the request body as-is, so Nimble's full parameter surface stays reachable. The values below are what the Search API accepts. LiteLLM does not validate them, so it is not the source of truth for allowed values. ```python theme={"system"} response = search( query="latest tech news", search_provider="nimble", max_results=5, focus="general", search_depth="deep", time_range="week", output_format="markdown", ) ``` | Parameter | Accepted values | | - | - | | `focus` | `general` (default), `news`, `location`, `coding`, `academic`, `geo`, `shopping`, `social`. Also takes a list of explicit Extract Template names. | | `search_depth` | `lite`, `fast`, `deep`. Some focus modes accept only `lite` and `deep`: see [Focus Modes](/nimble-sdk/web-tools/search#focus-modes). | | `time_range` | `hour`, `day`, `week`, `month`, `year`. Cannot be combined with `start_date` or `end_date`. | | `start_date` / `end_date` | `YYYY-MM-DD` or `YYYY`. | | `output_format` | `plain_text`, `markdown` (default), `simplified_html`. | | `content_type` | A **list**, never a string: `["pdf"]`, `["documents", "spreadsheets"]`. Only valid with `focus="general"`. | | `locale` | Language code such as `en`, `fr`, `de`. Defaults to `en`. | | `max_subagents` | 1 to 10. Applies to the `shopping`, `social`, and `geo` focus modes. | Full reference: [Search API](/api-reference/search/search). ### Cost tracking on the gateway `nimble/search` is priced in LiteLLM's own model cost map, so Nimble spend lands in LiteLLM's spend logs and dashboard alongside your model spend. No extra configuration is needed. LiteLLM's figure is an estimate, not your invoice. It applies one flat per-query rate to every search, which does not model how Nimble bills: * Nimble prices Search by `search_depth`, and `lite` costs less per search than `fast`. LiteLLM charges the same figure for both. * `search_depth="deep"` adds a live extraction surcharge on top, billed at Extract rates for each page scraped in real time. * Volume plans price differently from pay-as-you-go. See [Pricing](/nimble-sdk/admin/pricing) for the authoritative rates. ### Additional Resources The provider reference in LiteLLM's own documentation. Every parameter the provider can forward, with accepted values. Focus modes, search depth, and filtering explained. Reach Extract, Map, and Crawl, which LiteLLM does not cover. # LlamaIndex Source: https://docs.nimbleway.com/integrations/connectors/llamaindex Add real-time web search and cited deep research to LlamaIndex agents and workflows with the Nimble tool specs. ## Overview The [`llama-index-tools-nimble`](https://pypi.org/project/llama-index-tools-nimble/) package brings Nimble to [LlamaIndex](https://www.llamaindex.ai/). It ships two tool specs, and an agent can hold both at once. * **`NimbleToolSpec`**: live web search. One query, ranked results in seconds. * **`NimbleAgentToolSpec`**: deep research on a [Nimble Agent](/nimble-sdk/web-search-agents/overview). One run, a synthesized answer with per-claim citations and confidence. * **Citable output**: both return LlamaIndex `Document` objects with the source URLs in the document text, so the model can read and cite them. * **Agent-ready**: `to_tool_list()` plugs either spec into any LlamaIndex agent or workflow. * **RAG-friendly**: `Document` output drops straight into LlamaIndex indexes and query engines. ## Prerequisites The package requires `>=3.10,<4.0`. `llama-index-core>=0.13.0,<0.15` installs automatically. The agent examples also need an LLM integration, such as `llama-index-llms-openai`. Version 0.2.0 raised the SDK floor from `>=0.16,<1.0.0` to `>=1.2.0,<2.0.0`. If other code in the same environment targets `nimble-python` 0.x, plan to update it at the same time. Required by both tool specs. Get a key from the [dashboard](https://online.nimbleway.com/settings/api-keys), free trial available. The key is passed to the SDK client rather than stored on the tool spec, and the tool's own run errors are built without it. Deep research runs without one: the API provisions an agent per run. See [Agent identity](#agent-identity). ## Quick Start ```bash theme={"system"} pip install llama-index-tools-nimble ``` Add an LLM integration to run the agent examples: ```bash theme={"system"} pip install llama-index-llms-openai ``` Get your key from [Nimble's dashboard](https://online.nimbleway.com/settings/api-keys), then set it as an environment variable: ```bash theme={"system"} export NIMBLE_API_KEY="your-api-key" ``` Or pass it directly when constructing either tool spec: ```python theme={"system"} from llama_index.tools.nimble import NimbleToolSpec tool_spec = NimbleToolSpec(api_key="your-api-key") ``` `max_results` caps how many results come back. The default is 6. ```python theme={"system"} from llama_index.tools.nimble import NimbleToolSpec tool_spec = NimbleToolSpec() documents = tool_spec.search( "latest developments in web data infrastructure", max_results=5, ) for doc in documents: print(doc.metadata["title"], "-", doc.metadata["url"]) ``` ```text theme={"system"} Web Data Infrastructure for AI - https://nimbleway.com/... The State of Web Data in 2026 - https://example.com/... ``` ## How it works `to_tool_list()` turns a tool spec into LlamaIndex tools. `NimbleToolSpec` exposes `search`, `NimbleAgentToolSpec` exposes `run`. When the prompt needs live data, the model calls `search` for a quick lookup or `run` for a researched answer. Results come back as `Document` objects whose text carries the source URLs, so the model writes a grounded, citable answer. ## Search `NimbleToolSpec` wraps Nimble's [Search API](/nimble-sdk/web-tools/search) and returns each result as a `Document` carrying the page title and source URL. ### Build an AI agent Pass the tool spec to a LlamaIndex `FunctionAgent` with `to_tool_list()`. The agent can then search the web to answer questions with current facts: ```python theme={"system"} import asyncio from llama_index.core.agent.workflow import FunctionAgent from llama_index.llms.openai import OpenAI from llama_index.tools.nimble import NimbleToolSpec agent = FunctionAgent( tools=NimbleToolSpec().to_tool_list(), llm=OpenAI(model="gpt-4o-mini"), system_prompt="You are a research assistant. Use web search to answer with current facts.", ) async def main(): response = await agent.run("What has Nimble announced recently?") print(response) asyncio.run(main()) ``` ### Citable Document output Every result is a LlamaIndex `Document`. The title and source URL are embedded in the document text, so the agent can read and cite the source even though it never sees metadata. Each document's text has this shape: ```text theme={"system"} URL: <source url> <page content or snippet> ``` The title and URL are also available as metadata fields for programmatic use: | Field | Description | | - | - | | `metadata["title"]` | The page title | | `metadata["url"]` | The source URL | ### Use results in a LlamaIndex index Because `search()` returns `Document` objects, results drop straight into a LlamaIndex index for retrieval-augmented generation: ```python theme={"system"} from llama_index.core import VectorStoreIndex from llama_index.tools.nimble import NimbleToolSpec # Fetch live web results as Documents documents = NimbleToolSpec().search( "web data infrastructure trends", max_results=10, ) # Index them, then query with citable sources index = VectorStoreIndex.from_documents(documents) query_engine = index.as_query_engine() response = query_engine.query("Summarize the key trends.") print(response) ``` ### Search options <AccordionGroup> <Accordion title="query"> Argument to `search()`. The search query, supplied by the model when the tool is agent-driven. </Accordion> <Accordion title="max_results"> Argument to `search()`. Maximum results to return. Type `int`, default `6`, must be at least 1. Nimble's own cap is soft, so the tool trims the list to hold the contract. </Accordion> </AccordionGroup> `NimbleToolSpec(api_key=...)` is the only constructor argument. Search depth, focus, and output format are fixed at `lite`, `general`, and markdown, and the model cannot change them. ## Deep research with Nimble Agents `search()` is a synchronous building block: one query, one response, seconds. A [Nimble Agent](/nimble-sdk/web-search-agents/overview) run is a different capability. An autonomous agent plans, searches, reads, and cross-checks many sources, then returns a final answer with per-claim citations and confidence. | | Search | Nimble Agent run | | - | - | - | | Latency | Seconds | 10 to 30 seconds at `low`, 5 to 15 minutes at the default `high`. See [Efforts](/nimble-sdk/web-search-agents/efforts) | | Endpoint | `/v2/search` | `/v2/agents/*` | | Output | Ranked results, one `Document` each | One `Document`: final answer plus sources, citations, confidence | | Best for | Grounding a chat turn | Briefs, due diligence, monitoring, enrichment | ### Run one research task `NimbleAgentToolSpec` exposes a single `run` tool. It creates the run, polls until the run reaches a terminal status, then fetches and maps the result, all inside one call. ```python theme={"system"} from llama_index.tools.nimble import NimbleAgentToolSpec # timeout is raised past the 300 s default, because agent runs outlast it. agent_tool = NimbleAgentToolSpec(timeout=1800) # reads NIMBLE_API_KEY doc = agent_tool.run( "What are the leading approaches to LLM guardrails, and who builds them?", ) print(doc.text) # final answer + "Sources:" list print(doc.metadata["confidence"]) # high / medium / low / pre_existing print(doc.metadata["claims"]) # per-claim citations print(doc.metadata["web_search_agent_id"]) # the agent the API ran this on ``` Hand it to a LlamaIndex agent the same way as the search spec. Tell the model the tool is slow but thorough, so it reaches for it deliberately: ```python theme={"system"} import asyncio import os from llama_index.core.agent.workflow import FunctionAgent from llama_index.llms.openai import OpenAI from llama_index.tools.nimble import NimbleAgentToolSpec tool_spec = NimbleAgentToolSpec( agent_id=os.environ.get("NIMBLE_AGENT_ID"), timeout=1800, ) agent = FunctionAgent( tools=tool_spec.to_tool_list(), llm=OpenAI(model="gpt-4o-mini"), system_prompt=( "You are a research assistant. For questions that need a researched, " "synthesized answer, call the Nimble research tool (it is slow but " "thorough) and relay its answer together with the source URLs it cites." ), ) async def main(): response = await agent.run( "Research: what do photography reviewers consider the best " "mirrorless cameras for travel, and why? Cite your sources." ) print(response) asyncio.run(main()) ``` <Note> The model-facing tool schema is `{task, output_schema, input_data, sources}`. Agent identity, credentials, effort, and the deadline are set by the application at construction, so the model can never choose the agent or raise the cost tier. </Note> ### Agent identity `agent_id` is optional, and the choice decides the route: | Configuration | Route | Behavior | | - | - | - | | `agent_id` omitted | `POST /v2/agents/runs` | The API provisions an agent for the run and returns its id | | `agent_id="wsa_..."` | `POST /v2/agents/{agent_id}/runs` | The run executes on that preconfigured agent | Either way the **returned** `web_search_agent_id`, not the configured one, is the authority for the polling and result calls that follow. It lands in the document metadata, so an auto-provisioned agent stays reachable afterwards. A returned id that differs from a configured one is rejected rather than papered over. <Note> The tool is execution-only by design. It never creates, edits, or deletes agent instances, so account administration stays off the LLM surface. To pin runs to an agent, provision it in the [dashboard](https://online.nimbleway.com) or through `POST /v2/agents`, then pass its `wsa_...` id. </Note> ### Structured output and enrichment Three optional per-run controls shape the answer. They are passed through unchanged: the adapter never rewrites a caller's schema or source guidance. ```python theme={"system"} doc = agent_tool.run( "Find the founding year and headquarters for this company.", input_data={"company": "Acme", "domain": "acme.example"}, output_schema={ "type": "object", "properties": {"founded": {"type": "integer"}, "hq": {"type": "string"}}, }, sources={"prioritize": "regulatory filings", "avoid": "press releases"}, ) ``` <AccordionGroup> <Accordion title="output_schema"> A JSON Schema object the answer must match. Supply it to get structured JSON back in place of prose. The document text then holds pretty-printed JSON, and `metadata["output_type"]` reads `json`. </Accordion> <Accordion title="input_data"> An object, or a list of objects, to enrich. Each row holds known data about one entity to research. Enrichment payload only: it is never stored on the agent. </Accordion> <Accordion title="sources"> Source guidance. `allow` and `block` take lists of source objects; `prioritize` and `avoid` take free text. Any other key is rejected, so a model filling this tool schema cannot smuggle `skill`, `use_case`, or `agent_name` through a nested dict. </Accordion> </AccordionGroup> The schema above returns this shape, prose replaced by pretty-printed JSON and the `Sources:` list kept: ```text theme={"system"} { "hq": "<headquarters city>", "founded": <year> } Sources: - <source title> — <source url> (primary) - <source title> — <source url> (secondary) ``` ### Tool spec options Set on the `NimbleAgentToolSpec` constructor. Every parameter is optional. <AccordionGroup> <Accordion title="agent_id"> The Nimble Agent to run on, in the form `wsa_...`. Type `str`, default `None`, which lets the API provision an agent per run. </Accordion> <Accordion title="api_key"> Nimble API credentials. Falls back to `NIMBLE_API_KEY`. </Accordion> <Accordion title="effort"> `'low'`, `'medium'`, `'high'`, `'x-high'`, or the gated `'max'`. Default `None`, which omits the field so Nimble applies the agent or template default of `high`. Higher tiers research more sources and take longer. See [Efforts](/nimble-sdk/web-search-agents/efforts). This is an application setting, not a tool argument the model can raise. </Accordion> <Accordion title="gate_policy"> Treatment for gated values: `'reject'` (default) or `'degrade'`. `max` is a coming-soon custom-budget capability. Under `reject`, `effort="max"` raises before any run is created and points at the [Nimble product team](https://www.nimbleway.com/contact). Under `degrade`, the effective tier becomes `x-high` and a warning announces the substitution, so a different tier is never billed silently. </Accordion> <Accordion title="timeout"> Overall deadline in seconds for one `run` call, covering creation, polling, and result retrieval. Type `float`, default `300.0`. Raise it: the default is shorter than a typical run. See [Long runs and deadlines](#long-runs-and-deadlines). </Accordion> <Accordion title="poll_interval"> Seconds between status polls, and the pause before re-attempting a transient failure. Type `float`, default `10.0`. Shorter values are intended only for tests. </Accordion> <Accordion title="agent_name, skill, use_case"> Typed SDK hints applied to the run. `agent_name` names the run's generated agent, `skill` is a skill identifier, and `use_case` is `'research'`, `'enrichment'`, or `'dataset_building'`. All default to `None`. </Accordion> </AccordionGroup> ### Results and citations A completed run returns one `Document`. Its text is the final answer followed by a `Sources:` list, because an agent only ever sees a tool's stringified output, where document metadata is dropped. Citations have to live in the text for the model to read them. ```text theme={"system"} # Leading Approaches to LLM Guardrails LLM guardrail approaches are organized around a multi-layered safety pipeline spanning the model's lifecycle [1]: ## 1. Input-Level Defenses (Prompt Screening) Pre-processing mechanisms block or modify disallowed prompts before they reach the LLM, typically using classifiers, keyword/regex matching, or prompt-injection detectors [2]. ... Sources: - Survey on LLM Safety: Attacks, Defenses, Alignment, Metrics, and ... — https://link.springer.com/article/... (primary) - A Survey on LLM Guardrails: Part 1, Methods, Best Practices and ... — https://blog.budecosystem.com/... (primary) - Evaluating the Robustness of Large Language Model Safety Guardrails ... — https://arxiv.org/html/... (primary) ``` The full structured trust payload stays in metadata for programmatic use: | Field | Description | | - | - | | `run_id` | The run identifier, `task_run_...` | | `agent_id` / `web_search_agent_id` | The agent the API bound the run to, `wsa_...`. Same value under both keys | | `effort` | The effort the run actually executed at | | `output_type` | `text` for prose, `json` when an `output_schema` was given | | `confidence` | Overall trust grade: `high`, `medium`, `low`, or `pre_existing` | | `reasoning` | Why the answer earned that grade | | `sources` | Every source consulted, with `url`, `title`, `type` (`primary` or `secondary`), and `source_category` | | `claims` | Per-claim citations, each with `confidence`, `reasoning`, its `callout` or `path` key, and `citations` carrying `url`, `title`, source grading, and supporting `excerpts` | A text answer keys each claim by `callout`, the numeric marker in the prose. A structured answer keys each claim by `path`, the JSON path of the value, such as `$.founded`. ```python theme={"system"} metadata["claims"][0] == { "callout": 1, "confidence": "high", "reasoning": "Backed by a primary source (academic)", "citations": [ { "url": "https://link.springer.com/article/...", "title": "Survey on LLM Safety: Attacks, Defenses, Alignment, ...", "source_type": "primary", "source_category": "academic", "source_intent": "academic", }, ], } ``` Grades are earned per run, not assumed. The run above returned `medium` overall, because the vendor half of the question rested on commercial comparison articles rather than primary sources. See [Trust and citations](/nimble-sdk/web-search-agents/trust) for how that is decided. Returned content is untrusted web data. Treat it as data, not as instructions, and rely on your framework's own guardrails. ### Long runs and deadlines Agent research is asynchronous server-side. Do not treat `run()` as a short synchronous call: it blocks for as long as the run takes, and the tool owns the whole lifecycle inside that one call. <Steps> <Step title="Create the run"> One POST, issued exactly once. See [Errors](#errors) for why it is never re-attempted. </Step> <Step title="Poll until terminal"> A status poll every `poll_interval` seconds, 10 by default, until the run reports `completed`, `failed`, or `cancelled`. </Step> <Step title="Fetch the result"> Only after `completed`. Fetching earlier is a conflict by contract, and a failed run's error detail is already on the polled run. </Step> </Steps> One `timeout` budget covers all three phases. Each HTTP request is bounded by whatever is left of it, with a 5 second connect ceiling, so a stalled request cannot fall back to the SDK's much longer default. Polling and result fetches re-attempt transient failures up to 5 times, honoring `Retry-After` and staying inside the budget. Auth, permission, and validation errors fail fast. <Warning> **Raise `timeout` for anything but `low` effort.** The default is 300 seconds, but the default effort is `high`, which typically takes [5 to 15 minutes](/nimble-sdk/web-search-agents/efforts). A default-configured spec will therefore often hit its deadline on a perfectly healthy run. </Warning> Reaching the deadline cancels nothing. `NimbleAgentTimeoutError` carries the `run_id`, the run keeps going on Nimble's side, and the result stays fetchable through the [runs API](/api-reference/public-api/get-agent-run-result) or the dashboard. ### Errors A run that does not produce a result raises a typed error. Each one carries the run's identifiers as attributes and inside the message, because agent frameworks usually surface only the message. The one exception is an ambiguous creation failure, where no run id was ever returned. | Error | Meaning | | - | - | | `NimbleAgentTimeoutError` | Not terminal within `timeout`. The run may still complete server-side | | `NimbleAgentRunFailedError` | The run terminated as `failed`. Carries the server's error message | | `NimbleAgentRunCancelledError` | The run terminated as `cancelled` | | `NimbleAgentProtocolError` | Unknown status, malformed result, response identity mismatch, or persistent polling and result errors. The SDK exception is chained | | `NimbleAgentCreateAmbiguousError` | Run creation failed without a definite outcome. `run_id` is `None`, and `status_code` carries the HTTP status when there was one | All five subclass `NimbleAgentRunError`, so one `except` clause catches every run failure: ```python theme={"system"} from llama_index.tools.nimble import ( NimbleAgentRunError, NimbleAgentTimeoutError, NimbleAgentToolSpec, ) # x-high typically takes 15 to 30 minutes, so the deadline allows for that. agent_tool = NimbleAgentToolSpec(effort="x-high", timeout=2400) try: doc = agent_tool.run("Map the EU AI Act enforcement timeline.") except NimbleAgentTimeoutError as error: # Not a failure. Persist the ids however your app stores state, then # fetch the run later. `save_run_for_later` here is your own function. save_run_for_later(error.run_id, error.agent_id) except NimbleAgentRunError as error: print(error.status, error.run_id, error.agent_id) raise ``` <Note> `NimbleAgentCreateAmbiguousError` is the one error not to retry. Creation is sent once and never re-sent, so a run may already be underway with no id returned. A function-calling model reads a bare timeout as an ordinary transient, so exclude this error from any automatic tool-retry policy. Look for the earlier run in the [dashboard](https://online.nimbleway.com) instead. Requests that were definitely rejected, such as a bad key or a validation error, are safe to call again once fixed. </Note> ## Limitations * **Search and Nimble Agent runs** ship in this package. Extract, Map, and Crawl are not exposed as tool specs. Use the [Nimble Python SDK](/nimble-sdk/sdks/python). * **One blocking call per run.** There is no start-now, collect-later split, and no run event streaming (SSE). A run that outlives `timeout` is recovered through the runs API, not through this tool. ## Related features Easily confused with this package: | Feature | Where it lives | | - | - | | Agents, `/v2/agents/*` | This page. Current deep-research agents | | Extract Templates, `/v2/extract/templates` | The [Extract Template guide](/nimble-sdk/web-tools/extract/template). The old `/v1/agent` site scrapers were renamed to these, and are unrelated to Nimble Agents | | LlamaHub listing | The package carries LlamaHub registry metadata for both tool specs. No effect on how you install or import | | LlamaParse | LlamaIndex's own document parser. Not a Nimble product and not related to these tool specs | ## Resources <CardGroup> <Card title="PyPI Package" icon="python" href="https://pypi.org/project/llama-index-tools-nimble/"> `llama-index-tools-nimble` on PyPI. </Card> <Card title="GitHub Repository" icon="github" href="https://github.com/Nimbleway/llama-index-tools-nimble"> Source, README, and runnable examples. </Card> <Card title="v0.2.0 Release" icon="tag" href="https://github.com/Nimbleway/llama-index-tools-nimble/releases/tag/v0.2.0"> Deep research agents and agentless runs. </Card> <Card title="Agent" icon="robot" href="/nimble-sdk/web-search-agents/overview"> The deep-research capability behind the `run` tool. </Card> <Card title="Search API" icon="magnifying-glass" href="/nimble-sdk/web-tools/search"> Nimble's underlying search capability. </Card> <Card title="Nimble Python SDK" icon="python" href="/nimble-sdk/sdks/python"> Direct access to Extract, Map, Crawl, and agent administration. </Card> </CardGroup> # Mastra Source: https://docs.nimbleway.com/integrations/connectors/mastra Give a Mastra agent deep research on the live web. Start a Nimble Agent run in one turn, collect the cited answer in another, from any process. ## Overview Give a Mastra agent the ability to research the live web. Ask a question in one sentence. Get back a written answer with sources, per-claim citations, and a confidence grade. The [`@nimble-way/mastra`](https://www.npmjs.com/package/@nimble-way/mastra) package brings [Nimble Agent](/nimble-sdk/web-search-agents/overview) research into [Mastra](https://mastra.ai) as three typed tools. Nimble plans the research, searches, and opens pages. It reads JavaScript-heavy sites, cross-checks what it finds, and returns a cited answer. Your agent sends a task and collects the result. That research takes minutes, not milliseconds. A `high` effort run takes 5 to 15 minutes, far longer than a single agent turn. So by default none of the three tools block: the run keeps going server-side and the `runId` is the only handle needed to collect it. Bounded waiting is opt-in, and the `wait` option is the one way to make the result tool block. * **A cited answer from the live open web**: prose or JSON, with sources, per-claim citations with verbatim excerpts, and confidence grades, read at run time. * **Gets cheaper the more you run it**: a reused Nimble Agent remembers which sources worked. Recurring research runs up to 50% cheaper. * **Start now, collect later**: start the run in one turn, read the result in another turn, another process, or another day. * **The model never sees your credential**: the API key is resolved server-side and sent only as a request header. ### Which path to take Two ways to get Nimble research into a Mastra agent. Pick one in ten seconds. <CardGroup> <Card title="Nimble MCP Server" icon="plug" href="#using-the-nimble-mcp-server-instead"> The fastest start. No package to install. Tools arrive over the wire and the model calls them. Gives you the full Nimble tool surface, not just research. </Card> <Card title="This package" icon="cube" href="#quick-start"> Typed tools, a developer-controlled effort ceiling, server-side output schemas, and explicit create-failure semantics. Best when research is the job and cost control matters. </Card> </CardGroup> ## Prerequisites <AccordionGroup> <Accordion title="Node.js 22.13 or later" icon="node-js"> The package declares `engines.node >= 22.13.0`. </Accordion> <Accordion title="@mastra/core 1.56.0 or later, and zod" icon="cube"> Peer dependencies, supplied by your project: `@mastra/core` `^1.56.0` and `zod` `^3.25.1 || ^4.0.0`. The `zod` floor is `3.25.1` rather than `3.25.0` for a reason. `3.25.0` declares a `./v4` export whose target is missing from its published tarball, and Mastra's schema layer resolves that path, so the combination fails at import. The declared range excludes it, so npm refuses the install rather than letting it fail at run time. Verified against the published `0.1.1`: `zod` `3.25.1`, `3.25.76`, and `4.0.0` all work with `@mastra/core` `1.56.0`, as does `4.5.4` with `1.64.0`. </Accordion> <Accordion title="NIMBLE_API_KEY (server-side only)" icon="key"> Get a key from the [dashboard](https://online.nimbleway.com/settings/api-keys). See [Handling the API key](#handling-the-api-key) for the rules that keep it out of model input. </Accordion> <Accordion title="A model provider credential" icon="brain"> Mastra resolves the agent's `model` at run time, so the provider needs its own key in the environment. The examples here use `anthropic/claude-sonnet-4-6`, which reads `ANTHROPIC_API_KEY`. This is separate from `NIMBLE_API_KEY`. Nimble does the research; your model decides when to ask for it. </Accordion> <Accordion title="A Nimble Agent ID" icon="robot"> Create a Nimble Agent once, then reuse it. Its ID looks like `wsa_0a1b2c3d4e5f60718293a4b5c6d7e8f9`. A reused agent keeps its memory, so it gets faster, more accurate, and up to 50% cheaper on recurring work. Create one in the [dashboard](https://online.nimbleway.com) or with `POST /v2/agents`. The agent is developer configuration, not model input. The model can never choose which agent to run. </Accordion> </AccordionGroup> ## Quick Start <Steps> <Step title="Install"> ```bash theme={"system"} npm install @nimble-way/mastra@0.1.1 @mastra/core zod ``` `0.1.1` is the current release. It is published from CI over OIDC and carries a SLSA provenance attestation, so the artifact on npm can be traced to the commit and workflow that built it. </Step> <Step title="Set the environment"> ```bash theme={"system"} export NIMBLE_API_KEY="your-key" export NIMBLE_AGENT_ID="wsa_0a1b2c3d4e5f60718293a4b5c6d7e8f9" ``` Both are read server-side at execute time. Never put either in a committed file. </Step> <Step title="Attach the tools to an agent"> ```ts src/agents/researcher.ts theme={"system"} import { Agent } from "@mastra/core/agent"; import { createNimbleAgentTools } from "@nimble-way/mastra"; export const researcher = new Agent({ id: "researcher", name: "Researcher", instructions: "For deep research questions, start a Nimble agent run, tell the user the " + "runId, and fetch the result when they ask for it later.", model: "anthropic/claude-sonnet-4-6", tools: createNimbleAgentTools(), }); ``` `createNimbleAgentTools()` returns all three tools sharing one credential and one agent configuration. Pass `{ agentId, apiKey }` to override the environment. </Step> <Step title="Ask it something"> ```ts theme={"system"} const result = await researcher.generate( "Summarize how the EU AI Act is being enforced in 2026, with sources.", ); console.log(result.text); ``` The model calls `nimble-agent-start-run` and answers in seconds with a `task_run_…` ID, which it reports in `result.text`. The research keeps going server-side for minutes. Collect it later with the same ID. </Step> </Steps> ### Example Response A completed run, as the result tool returns it. This is the prose shape. An agent with a stored output schema returns `"type": "json"` with a `json` field instead of `text`, and its claims carry `path` rather than `callout`: ```json theme={"system"} { "ready": true, "runId": "task_run_9f8e7d6c5b4a39281706f5e4d3c2b1a0", "agentId": "wsa_0a1b2c3d4e5f60718293a4b5c6d7e8f9", "status": "completed", "effort": "low", "createdAt": "2026-09-08T10:00:00Z", "startedAt": "2026-09-08T10:00:01Z", "completedAt": "2026-09-08T10:02:00Z", "output": { "type": "text", "text": "The answer, with citation markers [1].", "trust": { "confidence": "high", "reasoning": "Two primary sources agree.", "sources": [ { "url": "https://example.com/a", "type": "primary", "source_category": "official", "title": "Official doc" } ], "claims": [ { "callout": 1, "confidence": "high", "reasoning": "Stated directly in the source.", "citations": [ { "url": "https://example.com/a", "excerpts": ["verbatim excerpt here"], "source_type": "primary" } ] } ] } } } ``` ## How it works <Steps> <Step title="The model starts a run"> `nimble-agent-start-run` sends `POST /v2/agents/{agent_id}/runs` and returns immediately with a real `task_run_…` ID. The turn is never blocked for the run's duration. </Step> <Step title="The run proceeds server-side"> Nimble plans the research, searches, opens pages, and cross-checks findings. Minutes, not milliseconds. </Step> <Step title="Anything with the runId can check it"> `nimble-agent-run-status` returns an instant snapshot. `nimble-agent-run-result` returns the answer, or `{ ready: false }` while the run is still working. </Step> <Step title="The answer arrives with its evidence"> A completed run returns prose or structured JSON, plus `trust`: sources, per-claim citations with verbatim excerpts, and confidence grades. </Step> </Steps> ### The three tools | Tool id | Model-facing input | What it returns | | - | - | - | | `nimble-agent-start-run` | `task` (required), `effort` (optional) | `runId`, `agentId`, `interactionId`, `status`, `effort`, `createdAt` | | `nimble-agent-run-status` | `runId` | `status`, `isActive`, and run timestamps. Instant, never waits | | `nimble-agent-run-result` | `runId` | The answer with `trust`, or `{ ready: false }` while still working | The factory returns them keyed `nimbleAgentStartRun`, `nimbleAgentRunStatus`, and `nimbleAgentRunResult`. Those three inputs are the complete model-facing surface. The credential, the agent, the effort ceiling, the output schema, and the source boundary are all developer configuration. ### The resumable lifecycle The `runId` is the only handle. Any process configured with the same agent can check or collect a run it did not start. ```ts theme={"system"} // Turn 1 - the agent starts a run and answers immediately: // start -> { runId: "task_run_…", status: "queued", … } // Turn 2 (seconds or hours later, same or a different process): // status -> { status: "running", isActive: true, … } // Turn 3: // result -> { ready: false, status: "running", … } // still working, not an error // result -> { ready: true, status: "completed", output: { type: "text", text, trust } } ``` ## Parameters <AccordionGroup> <Accordion title="agentId" icon="robot"> The Nimble Agent to run, for example `wsa_0a1b2c3d4e5f60718293a4b5c6d7e8f9`. Defaults to `process.env.NIMBLE_AGENT_ID`. Resolved at execute time, so the model can never choose the agent. Missing it raises `NimbleConfigError: Missing Nimble agent id`. </Accordion> <Accordion title="apiKey" icon="key"> Nimble API key. Defaults to `process.env.NIMBLE_API_KEY`. Read server-side at execute time and sent only as a request header. Missing it raises `NimbleConfigError: Missing Nimble API key`. </Accordion> <Accordion title="effort and effortCap" icon="gauge"> `effort` is the tier used when the model does not choose one. Leave it unset and the agent instance's own configured default applies, which is the recommendation. `effortCap` is the ceiling on what the *model* may request. It defaults to `high`, so a model cannot unilaterally reach the `x-high` and `max` cost tiers. A developer-set `effort` is not clamped. See [effort tiers](/nimble-sdk/web-search-agents/efforts) for what each tier does and costs. Verified behaviour, reading the request body the package sends: | Configuration | Model requests | Sent to the API | | - | - | - | | default cap (`high`) | `max` | `high` | | default cap (`high`) | `x-high` | `high` | | `effortCap: "medium"` | `max` | `medium` | | `effortCap: "medium"` | `low` | `low` | | `effort: "max"`, `effortCap: "medium"` | nothing | `max` | | nothing set | nothing | `effort` omitted | Those are the request bodies the package sends. `max` is a custom-budget tier that is [coming soon](/nimble-sdk/web-search-agents/efforts), so the API may reject it even though the package forwards it. Ask for `x-high` if you want the top available tier now. </Accordion> <Accordion title="outputSchema" icon="brackets-curly"> A JSON Schema sent as the Agent API `output_schema`. Use it when the run must return structured data. Keep this in developer configuration. Do not ask the model to author JSON, source URLs, or excerpts in its prompt. ```ts theme={"system"} import { nimbleAgentStartRunTool } from "@nimble-way/mastra"; const startResearch = nimbleAgentStartRunTool({ outputSchema: { type: "object", required: ["facts", "recommendation"], properties: { facts: { type: "array", items: { type: "object" } }, recommendation: { type: "string" }, }, }, }); ``` A completed run then returns `output.type: "json"` with `output.json` instead of `output.text`. </Accordion> <Accordion title="sources" icon="filter"> Server-enforced source guidance, sent as the Agent API `sources`. ```ts theme={"system"} import { nimbleAgentStartRunTool } from "@nimble-way/mastra"; const startResearch = nimbleAgentStartRunTool({ sources: { allow: [ { title: "Official documentation", domains: ["docs.example.com"] }, { title: "Official repository", domains: ["github.com"] }, ], }, }); ``` Rejected before the run is created: an empty `allow`, a group with a blank `title`, a group with an empty `domains` array, and a blank domain string. </Accordion> <Accordion title="wait" icon="clock"> Opt-in bounded waiting on the result tool. Absent, which is the default, the tool never blocks. ```ts theme={"system"} import { nimbleAgentRunResultTool } from "@nimble-way/mastra"; const result = nimbleAgentRunResultTool({ wait: { pollIntervalMs: 10_000, timeoutMs: 600_000 }, }); ``` Defaults are `timeoutMs: 300_000` and `pollIntervalMs: 10_000`, with a 100 ms floor. Runs take minutes, so polling faster only spends requests. Waiting respects Mastra's per-call `AbortSignal`. When the timeout elapses the tool returns `{ ready: false }` and the run keeps going server-side. A bounded wait never invents a terminal status: it returns the last authoritative one. If the initial status request itself reaches the deadline, `status` is `unknown` and activity is omitted, because no lifecycle snapshot was received. </Accordion> <Accordion title="clientOptions" icon="sliders"> Options forwarded to the Nimble client: `baseURL`, `fetch`, `timeout`, and `maxRetries`. `maxRetries` applies to status and result reads only. Run creation is always sent with `maxRetries: 0`, whatever this value is. </Accordion> </AccordionGroup> ### Per-tool configuration Individual factories are exported when the three tools need different settings: ```ts theme={"system"} import { nimbleAgentStartRunTool, nimbleAgentRunStatusTool, nimbleAgentRunResultTool, } from "@nimble-way/mastra"; const tools = { startResearch: nimbleAgentStartRunTool({ effortCap: "medium" }), researchStatus: nimbleAgentRunStatusTool(), researchResult: nimbleAgentRunResultTool({ wait: { timeoutMs: 600_000 } }), }; ``` ## A long-running research example This is the shape the package is built for. The run outlives the turn that started it. ```ts src/research.ts theme={"system"} import { Agent } from "@mastra/core/agent"; import { createNimbleAgentTools } from "@nimble-way/mastra"; export const researcher = new Agent({ id: "researcher", name: "Researcher", instructions: "Start a Nimble run for deep research questions and report the runId to the " + "user. When the user asks for the answer, fetch the result by runId. If the " + "result is not ready, say so and keep the runId. Never start a second run " + "for a task that already has a runId. If starting a run fails without " + "returning a runId, report the failure and stop. Do not try again.", model: "anthropic/claude-sonnet-4-6", tools: createNimbleAgentTools({ effortCap: "high" }), }); // Turn 1: the model starts the run and answers in seconds. const started = await researcher.generate( "Summarize how the EU AI Act is being enforced in 2026, with sources.", ); console.log(started.text); // carries the task_run_… id persist it // Turn 2, minutes later, in the same process or a different one: const collected = await researcher.generate( "Fetch the result for run task_run_9f8e7d6c5b4a39281706f5e4d3c2b1a0.", ); console.log(collected.text); ``` Persist the `runId` wherever your application keeps state. It is the only handle needed, and it stays valid across processes and restarts. <Warning> Keep the "never start a second run" clause in the instructions. A bounded `wait` can expire before a `high` run completes, and the result tool returns `{ ready: false }` whenever it does. That is expected, not a failure. Without the clause, restarting is an available response to a not-ready result, and each restart is a new billed run. Nothing in `effortCap` limits how many runs a model may create. </Warning> <Note> The three tools are built for the model to call. To drive the lifecycle yourself, server-side and without a model in the loop, use the exported `createNimbleClient(apiKey)` and call `nimble.agents.runs.create`, `.get`, and `.result` directly. </Note> ## Citations and trust metadata Every completed run carries a `trust` object: * `confidence` and `reasoning`: the run's own grade of its answer, and why. * `sources`: every source used, with `type` and `source_category`. * `claims`: per-claim confidence, plus `citations` carrying the `url` and the **verbatim excerpts** the claim rests on. In a prose answer, a `callout` number ties the claim to its marker in the text. Under an [output schema](#parameters) there is no prose, so claims carry a `path` instead: the JSON path of the value they support. Read whichever is present, not `callout` alone. `trust` is passed through verbatim. `snake_case` field names are preserved and unknown future fields are kept, so nothing is silently dropped and prose citation markers stay aligned with the text. <Warning> Trust-provided source URLs, claims, citations, and excerpts are the authoritative record. Render them from `trust`. Never ask the model to restate them as its own output fields, and never treat a model-authored URL as a citation. </Warning> ## Handling the API key `NIMBLE_API_KEY` is a server-side credential. The package resolves it at execute time and sends it only as a request header. Verified against the published package, reading the requests it actually sends: the key appears in `Authorization: Bearer …` and nowhere else. It is absent from all three tool input schemas, absent from tool outputs, and scrubbed from error messages. <Warning> The key must never appear in a model schema, a tool input, a tool output, client state, a log line, a prompt, or an example. The model's entire input surface is `task`, `effort`, and `runId`. Keep it that way. Do not pass the key through a URL. A credential in a URL leaks into server access logs, browser history, and `Referer` headers sent to third parties. </Warning> If you inject your own client with `client`, attribution headers and credential handling become your concern. Pass the matching `apiKey` too, or server error details are withheld, because the package cannot prove they are safe to expose. ## Failure and not-ready behaviour An active run is never an error. Two cases matter, and the package treats them differently on purpose. <AccordionGroup> <Accordion title="A run that is still working" icon="hourglass"> The result tool returns `{ ready: false }` with the run's `status` and `isActive`. Not an error. Check again later. **Branch on `ready`, never on `status`.** A not-ready payload can carry any status, including a terminal one, and it never carries `output`. The same HTTP 409 on a completed run reports differently depending on `wait`: | Configuration | Returned | | - | - | | no `wait` | `ready: false`, `status: "running"`, `isActive: true` | | with `wait` | `ready: false`, `status: "completed"`, `isActive: false`, no `output` | So `if (status === "completed") render(output.text)` throws on a payload that is entirely legal. A `status` reported after a `wait` deadline is also the last snapshot taken rather than a fresh read, so a run that failed after that snapshot can still report `running`. Confirm with the status tool, which never waits, before treating a run as alive. </Accordion> <Accordion title="A run that terminally failed or was cancelled" icon="triangle-exclamation"> Throws `NimbleAgentRunError` with `reason: "failed"`, the server's message, and the `runId` preserved so the run can still be inspected or reported. Verified message shape: `Nimble agent run task_run_… failed: <server message>`. </Accordion> <Accordion title="Run creation failed, definitely" icon="circle-xmark"> A definite 4xx such as 429 gives `createOutcome: "not-created"` and `reason: "request"`. No run exists. Retry only after the underlying condition clears. </Accordion> <Accordion title="Run creation failed, ambiguously" icon="circle-question"> A timeout, 408, 409, 5xx, or a dropped connection gives `createOutcome: "unknown"`. The run **may** have been created server-side. **An ambiguous create is never retried automatically.** Run creation is billed and not idempotent, so the POST is sent once with `maxRetries: 0`. The transient-retry policy is disabled for creation only. Status and result reads keep normal retries. Reconcile before creating again: list recent runs for the agent, or check the dashboard. An automatic retry here bills a second run. The thrown error carries **no `runId`**, because none was returned, so a run that was created cannot be reconciled by ID. Reconcile by agent and timestamp instead. `maxRetries: 0` only removes the *transport* retry. A model that is told to retry a failed start reinstates it one layer up, and the package cannot tell that apart from a new task. Keep the "do not try again" clause in the [example instructions](#a-long-running-research-example). </Accordion> <Accordion title="An out-of-contract payload" icon="file-circle-exclamation"> Surfaces as `reason: "protocol"` on `NimbleAgentRunError`, not a `TypeError`. </Accordion> </AccordionGroup> ## Mastra background tasks Mastra can run a tool call as a [background task](https://mastra.ai/docs/long-running-agents/background-tasks), so the agent turn returns before the tool finishes. This is optional. By default the Nimble tools already return immediately. Reach for it when you want Mastra to hold the thread open and re-invoke the agent once the answer lands. Enable it on the Mastra instance, then opt the tool in: ```ts theme={"system"} import { Mastra } from "@mastra/core"; export const mastra = new Mastra({ agents: { researcher }, backgroundTasks: { enabled: true, globalConcurrency: 10, perAgentConcurrency: 5, }, }); ``` With `untilIdle`, the stream stays open until the background task completes and the agent is re-invoked with the result: ```ts theme={"system"} const stream = await researcher.stream("Research the EU AI Act enforcement record", { memory: { thread: "t1", resource: "u1" }, untilIdle: true, }); ``` You can also look a task up directly: ```ts theme={"system"} const task = await mastra.backgroundTaskManager?.getTask(taskId); const listed = await mastra.backgroundTaskManager?.listTasks({ status: "running", agentId: "researcher", }); const tasks = listed?.tasks ?? []; ``` <Warning> Mastra's `taskId` and Nimble's `runId` are different identifiers for different things. The `taskId` tracks Mastra's local execution of the tool call. The `runId` is the research run itself, and it is the only handle that survives a restart, a redeploy, or a move to another process. Persist and surface the `runId`. A Mastra `taskId` cannot collect a Nimble run, and a task that fails or is evicted does not stop or reclaim the run it started. </Warning> ## Nimble research or Mastra's built-in web search Mastra ships its own `webSearchTool`. It is not an alternative to Nimble research, and the difference is worth ten seconds of thought. | | Mastra `webSearchTool` | Nimble Agent run | | - | - | - | | Who searches | The active model provider | Nimble, independent of your model | | Provider support | OpenAI, Anthropic, Google, and xAI only | Any model, including local and open-weight | | Shape | One in-turn search, milliseconds to seconds | A planned multi-step run, minutes | | Evidence | Whatever the provider returns | Sources, per-claim citations with verbatim excerpts, confidence grades | | Reuse | None | A reused agent keeps memory across runs | | Control | Provider-defined | Developer-set effort ceiling, `output_schema`, source allowlist | `webSearchTool` resolves the active model at runtime and delegates to that provider's own search. When the provider has none, it throws a `MastraError` with the id `WEB_SEARCH_UNSUPPORTED_PROVIDER`. Reach for `webSearchTool` for a quick in-turn lookup on a supported provider. Reach for a Nimble run when the answer has to be researched rather than recalled, has to carry its evidence, or has to be reproducible across models. They coexist: attach both and let the instructions route between them. ## Using the Nimble MCP Server instead If you want Nimble tools in a Mastra agent without adding a dependency, connect the [Nimble MCP Server](/integrations/mcp-server/mcp-server) over Streamable HTTP: ```ts theme={"system"} import { MCPClient } from "@mastra/mcp"; const mcp = new MCPClient({ servers: { nimble: { url: new URL("https://mcp.nimbleway.com/mcp"), requestInit: { headers: { Authorization: `Bearer ${process.env.NIMBLE_API_KEY}` }, }, }, }, }); const tools = await mcp.listTools(); ``` <Warning> Pass the credential in the `Authorization` header, through `requestInit`, never in the URL. A key in a URL leaks into server access logs, browser history, and `Referer` headers sent to third parties. Header values are not logged the same way. </Warning> This is the shortest path, and it gives you the whole Nimble tool surface rather than research alone. What it does not give you is the package's developer-side controls. Those are the effort ceiling that stops a model reaching `max`, the server-enforced output schema and source allowlist, and the explicit `not-created` against `unknown` distinction on a failed create. Choose the MCP server for an interactive agent that needs breadth. Choose the package when research is the job and you need those controls. The MCP server page covers the full transport options, the authentication options including OAuth, and the complete tool table. This section does not repeat them. ## Attribution Every request the package makes carries `X-Client-Source: mastra`. The package sets this itself, on run creation and on every status and result read. There is nothing to configure, and nothing for you to set. The value is a constant. It carries no credential and no user-identifying data. The one exception is an injected `client`. The package then makes no requests of its own, so attribution travels only if your client sets the header. ## Next Steps <CardGroup> <Card title="Agent" icon="robot" href="/nimble-sdk/web-search-agents/overview"> What a Nimble Agent is, how effort tiers work, and how agent memory makes recurring research cheaper. </Card> <Card title="Nimble MCP Server" icon="plug" href="/integrations/mcp-server/mcp-server"> Transports, the full authentication options including OAuth, and the complete tool table. </Card> <Card title="Trust" icon="shield-check" href="/nimble-sdk/web-search-agents/trust"> How sources, per-claim citations, excerpts, and confidence grades are produced. </Card> <Card title="Package on npm" icon="npm" href="https://www.npmjs.com/package/@nimble-way/mastra"> Release notes, the typed exports, and the changelog. </Card> </CardGroup> # NVIDIA AI-Q Source: https://docs.nimbleway.com/integrations/connectors/nvidia-aiq Nimble ships as a built-in web search tool in the NVIDIA AI-Q Blueprint: live web context for enterprise research agents, configured in one YAML block. ### Overview The [NVIDIA AI-Q Blueprint](https://github.com/NVIDIA-AI-Blueprints/aiq) is an open reference implementation for enterprise research agents. It connects agents to internal data, reasons over it with NVIDIA-served models, and returns sourced business insight. It runs on the NeMo Agent Toolkit, so its tools are `nat` functions declared in a workflow YAML. Nimble ships as the `nimble_web_search` function, a built-in data-source plugin. Calls go to Nimble's [Search API](/nimble-sdk/web-tools/search) and come back as `<Document>` blocks the agent can cite. No fork, no custom tool code. The provider ships in **AI-Q v2.2.0 and later**. ### What the integration unlocks Any AI-Q agent gains live web retrieval without tool-specific code. The model passes a natural-language question, and the provider returns ranked results as escaped XML: ```xml theme={"system"} <Document href="https://docs.nvidia.com/cuda/"> <title> CUDA Toolkit Documentation Find installation instructions, launch highlights, programming guides, compiler documentation, API references, CUDA libraries, profiling tools, samples, and ... ``` Every block carries an absolute `href`, and the provider drops results without one. Nimble handles anti-bot evasion, JavaScript rendering, and geo-targeting underneath. ### Quick Start #### 1. Get a Nimble API key Get your API key from [Nimble's dashboard](https://online.nimbleway.com/settings/api-keys) (free trial available) and export it in the environment AI-Q runs in: ```bash theme={"system"} export NIMBLE_API_KEY="your-api-key" ``` Set this before starting AI-Q. Without it the tool still registers and logs one warning, then returns an error the first time the agent searches. Startup looks healthy while the agent cannot search, so treat that warning as a failure. #### 2. Install AI-Q with the provider ```bash theme={"system"} git clone https://github.com/NVIDIA-AI-Blueprints/aiq.git && cd aiq ./scripts/setup.sh ``` `setup.sh` creates the virtual environment and installs every data-source plugin, `nimble_web_search` included. To add it to a checkout you already have: ```bash theme={"system"} uv pip install -e ./sources/nimble_web_search ``` #### 3. Add the function to your workflow Declare `nimble_web_search` under `functions` in your workflow YAML: ```yaml theme={"system"} functions: web_search_tool: _type: nimble_web_search max_results: 5 search_depth: lite country: US locale: en ``` Then reference `web_search_tool` from the agent that should search. #### 4. Confirm the tool registered ```bash theme={"system"} nat info components --types function | grep nimble_web_search ``` A row for `nimble_web_search` means the plugin entry point resolved. ### Configuration **Optional in config, required in practice.** Falls back to the `NIMBLE_API_KEY` environment variable. A key set here is passed straight to the SDK and never written to the process environment. **Optional.** Results to return, `1` to `100`. Defaults to `5`. Sent unclamped, so Nimble validates the range and reports the error. **Optional.** `lite`, `fast`, or `deep`. Defaults to `lite`. * `lite`: title, URL, and description only. Lowest latency and lowest token cost. * `fast`: rich content at low latency, and the depth Nimble recommends for agent loops. * `deep`: full page content per result. Returns the raw page, including navigation and image data, so pair it with `max_content_length`. See [Search Depth](/nimble-sdk/web-tools/search-depth) for the tradeoffs and [Pricing](/nimble-sdk/admin/pricing) for what each depth costs. **Optional.** Defaults to `general`. Leave it there for research and general agent use. The provider accepts `general`, `news`, `location`, `shopping`, `geo`, and `social`, and validates the value at config load, so a typo surfaces at startup with the valid set named in the error. `focus` is a workflow setting, not an agent-chosen parameter. The model only supplies a query, so a general research question cannot silently reroute. **Optional.** Characters kept per result, truncated with an ellipsis. Defaults to `10000`. Set to `None` to disable truncation. Matters most with `search_depth: deep`. **Optional.** Attempts on a transient failure, `1` to `10`. Defaults to `3`, with exponential backoff capped at 30 seconds. Authentication and authorization errors return immediately instead of retrying. **Optional.** `country` is an ISO 3166 code for geo-targeted results, default `US`. `locale` is a language code, default `en`. NVIDIA maintains the full parameter table in its own [configuration reference](https://docs.nvidia.com/aiq-blueprint/latest/customization/configuration-reference.html#nimble-web-search). ### Additional Resources NVIDIA's own parameter table for `nimble_web_search`. The blueprint, the provider source, and its tests. The endpoint behind the provider: parameters and response format. Reach Extract, Map, and Crawl over the Model Context Protocol. # OGX Source: https://docs.nimbleway.com/integrations/connectors/ogx Nimble is a built-in web_search provider in OGX, the open agentic API server: grounded, live web search for any model it serves. ### Overview [OGX](https://github.com/ogx-ai/ogx) is an open, production-ready agentic API server and the successor to Llama Stack. It runs the agentic loop (inference, tool calling, RAG, and conversation management) behind the native APIs of OpenAI, Anthropic, and Google. Nimble ships as the `remote::nimble-search` provider, registered under the `builtin::websearch` toolgroup alongside the other web search backends. When a model calls the `web_search` tool, OGX routes the request to Nimble's [Search API](/nimble-sdk/web-tools/search) and returns ranked, grounded results with source URLs. No fork, no plugin. The provider ships in **OGX v1.1.0 and later**. ### Web search for any model Any model OGX serves can search the web through Nimble, with no model-specific code. A `web_search` tool call resolves to `builtin::websearch`, which dispatches to the registered `remote::nimble-search` provider automatically. Nimble handles anti-bot evasion, JavaScript rendering, and geo-targeting underneath, so the model gets back clean, ranked results to ground its answer in. ### Quick Start #### 1. Get a Nimble API key Get your API key from [Nimble's dashboard](https://online.nimbleway.com/settings/api-keys) (free trial available) and export it in the environment OGX runs in: ```bash theme={"system"} export NIMBLE_API_KEY="your-api-key" ``` #### 2. Register the provider and start OGX Add Nimble to the `tool_runtime` section of your distribution config (for example, the `starter` distribution): ```yaml theme={"system"} tool_runtime: - provider_id: nimble-search provider_type: remote::nimble-search config: api_key: ${env.NIMBLE_API_KEY:=} max_results: 3 search_depth: lite country: US locale: en ``` Then start the server: ```bash theme={"system"} ogx run starter ``` On boot, OGX auto-registers the toolgroup: `provider_id=nimble-search toolgroup_id=builtin::websearch`. #### 3. Search the web from a model Call the Responses API with the `web_search` tool enabled. OGX routes the call through `builtin::websearch` to Nimble: ```bash theme={"system"} curl http://localhost:8321/v1/responses \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-4o-mini", "input": "What were the biggest AI model releases this month? Cite your sources.", "tools": [{"type": "web_search"}], "stream": false }' ``` The response contains a `web_search_call` item (status `completed`), followed by an assistant message grounded in live Nimble results with source URLs. ### Configuration The `remote::nimble-search` provider accepts these keys under `config`: **Required.** Your Nimble API key. Provide it once via the `NIMBLE_API_KEY` environment variable (referenced as `${env.NIMBLE_API_KEY:=}`), or per request via the `X-OGX-Provider-Data` header: ```json theme={"system"} {"nimble_search_api_key": "your-api-key"} ``` A per-request header value overrides the configured key. **Optional.** Maximum number of results to return. Defaults to `3`. **Optional.** Controls the speed-vs-richness tradeoff. Defaults to `lite`. * `lite`: titles, URLs, and snippets. Lowest latency, best for broad discovery. * `deep`: full real-time page extraction for each result. Higher latency, richer content, and may require a higher Nimble tier. If your account lacks it, the provider returns a clear "tier not enabled" message rather than failing. See [Search Depth](/nimble-sdk/web-tools/search-depth) for details. **Optional.** ISO-2 country code for geo-targeted results (e.g. `US`, `GB`). Defaults to `US`. Can also be set per request via the model's location hint. **Optional.** Language/locale code for results (e.g. `en`). Defaults to `en`. ### Using Nimble over MCP (alternative) On an OGX release earlier than v1.1.0, or if you prefer an MCP-based setup, OGX can reach Nimble through its built-in Model Context Protocol support, with no provider configuration needed. Point a Responses `mcp` tool at Nimble's hosted MCP server: ```bash theme={"system"} curl http://localhost:8321/v1/responses \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-4o-mini", "input": "What are the latest changes to the EU AI Act? Search the web and cite sources.", "tools": [{ "type": "mcp", "server_label": "nimble", "server_url": "https://mcp.nimbleway.com/mcp", "authorization": "your-api-key" }], "stream": false }' ``` Pass the raw API key in the tool's `authorization` field. OGX adds the `Bearer` prefix itself. The hosted server exposes the `nimble_search` tool alongside Nimble's other capabilities. See the [Nimble MCP server docs](/integrations/mcp-server/mcp-server) for the full tool list and authentication details. ### Additional Resources The open agentic API server. The Search endpoint behind the provider: parameters and response format. Reach Nimble over the Model Context Protocol. The `remote::nimble-search` reference in OGX's own documentation. # Omnigent Source: https://docs.nimbleway.com/integrations/connectors/omnigent Omnigent ships three Nimble capabilities: a web_search provider, Extract Templates for structured scraping, and Agent API V2 for cited deep research. ## Overview