> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nimbleway.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Running at Scale

> One agent, many runs: batch sizes, concurrency, retries and the ledger that makes a big job finish

A Web Search Agent is built to be created once and run many times. Most agents are created, run once and abandoned, which throws away the skill, goals and sources that were tuned for the task. Scale is one agent, a list of inputs, and a loop that batches, submits, collects and tops up.

## One agent, many runs

Create the agent once with a stable name, then run it per batch. `agent_name` on `agents.run` creates the agent on the first call and reuses it on every later call. Override `sources` or `output_schema` per run only when a batch needs it; the agent's stored configuration is unchanged.

```python Python theme={"system"}
run = nimble.agents.run(
    agent_name="eu-ai-infra-enricher",
    use_case="enrichment",
    effort="high",
    input="Fill the missing fields for the companies in input_data.",
    input_data=batch,          # about 5 rows for 6 researched fields at high
    output_schema=SCHEMA,      # array root: a list in input_data needs a table shape
)
```

```typescript TypeScript theme={"system"}
const run = await nimble.agents.run({
  agent_name: "eu-ai-infra-enricher",
  use_case: "enrichment",
  effort: "high",
  input: "Fill the missing fields for the companies in input_data.",
  input_data: batch,          // about 5 rows for 6 researched fields at high
  output_schema: SCHEMA,      // set once; later runs inherit it
});
```

```go Go theme={"system"}
run, err := client.Agents.Run(ctx, nimble.AgentRunParams{
    AgentName:    param.NewOpt("eu-ai-infra-enricher"),
    UseCase:      nimble.AgentRunParamsUseCaseEnrichment,
    Effort:       nimble.AgentRunParamsEffortHigh,
    Input:        "Fill the missing fields for the companies in input_data.",
    InputData:    nimble.AgentRunParamsInputDataUnion{OfMapOfAnyMap: batch},
    OutputSchema: schema,
})
```

```bash CLI theme={"system"}
nimble agents run \
  --agent-name "eu-ai-infra-enricher" \
  --use-case enrichment \
  --effort high \
  --input "Fill the missing fields for the companies in input_data." \
  --input-data "$BATCH_JSON" \
  --output-schema "$SCHEMA_JSON"
```

```bash cURL theme={"system"}
curl -X POST 'https://sdk.nimbleway.com/v2/agents/runs' \
  --header 'Authorization: Bearer <YOUR-API-KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "agent_name": "eu-ai-infra-enricher",
    "use_case": "enrichment",
    "effort": "high",
    "input": "Fill the missing fields for the companies in input_data.",
    "input_data": '"$BATCH_JSON"',
    "output_schema": '"$SCHEMA_JSON"'
  }'
```

## Batch sizes

| Use case | Per run | Why |
| - | - | - |
| Enrichment | About 5 rows with 6 researched fields at `high`; 10 rows with 2 or 3 fields; 1 to 3 rows at `x-high` for hard fields | Hard cap is 20 rows and 200 input cells, but the practical limit is what one run finishes: a 10-row, 6-field batch at `high` ran 21 minutes and researched 5 rows; the same fields on 5 rows took 4.5 minutes and finished |
| Dataset building | Aim for 50 to 100 rows per run, scoped by region, category or site section | One run over a whole corpus truncates; many scoped runs merge cleanly |
| Research | 1 entity per run | Multi-entity research prompts truncate silently |

## Discovery, then enrichment

Two agents beat one wide one. The first finds entities and returns identity fields (name, domain, one or two filters). The second takes those rows as `input_data` and fills the columns. Each stays narrow, each can be re-run on its own, and the join key is yours.

```text theme={"system"}
discovery agent   →  [ {company, domain, hq_country, latest_round} × 40 ]
                          │  split into batches of 10
enrichment agent  →  [ {…, employee_band, ceo_name, ceo_source_url, has_pricing_page} × 10 ] × 4
```

## An orchestration loop

The shape of a loop that finishes. Adapt the storage and retry policy to your stack.

```python Python theme={"system"}
import time
from nimble_python import Nimble

nimble = Nimble(api_key="YOUR-API-KEY")
AGENT = "eu-ai-infra-enricher"
CONCURRENCY = 10            # account default is 30 concurrent agents
BATCH = 5                   # rows per run; what one high run finishes for ~6 fields
STUCK_AFTER = 30 * 60       # seconds before a run is treated as lost

def chunks(rows, n):
    for i in range(0, len(rows), n):
        yield rows[i:i + n]

def submit(batch):
    return nimble.agents.run(
        agent_name=AGENT,
        input="Fill the missing fields for the companies in input_data.",
        input_data=batch,
    )

pending = list(chunks(todo, BATCH))      # todo = rows not yet in your ledger
active = {}                              # run_id -> (agent_id, batch, started_at)
done, failed = [], []

while pending or active:
    while pending and len(active) < CONCURRENCY:
        batch = pending.pop(0)
        run = submit(batch)
        active[run.id] = (run.web_search_agent_id, batch, time.time())

    time.sleep(15)
    for run_id, (agent_id, batch, started) in list(active.items()):
        run = nimble.agents.runs.get(run_id, agent_id=agent_id)
        if run.is_active:
            if time.time() - started > STUCK_AFTER:
                failed.append(batch); del active[run_id]      # resubmit later, once
            continue
        del active[run_id]
        if run.status != "completed":
            failed.append(batch)
            continue
        result = nimble.agents.runs.result(run_id, agent_id=agent_id)
        done.extend(result.output.content)                    # rows, keyed on domain

# ledger: which input domains came back, which did not
returned = {row["domain"] for row in done}
missing = [row for b in failed for row in b] + [r for r in todo if r["domain"] not in returned]
# top up: resubmit `missing` with the returned rows as an exclusion list
```

Points that matter more than the code:

* **Concurrency below the account limit**, leaving room for other traffic. Default limits are 30 concurrent agents and 200 runs per day.
* **A stuck threshold**. Runs that stay active past 30 minutes at `high` are rare and usually a platform issue. Resubmit once; do not loop forever.
* **Idempotent submission**. The agent name is stable, so a retried batch hits the same agent with the same configuration.
* **A ledger keyed on your identifier**, not on run ids. The API reports runs, not coverage of your list.
* **Dedupe after every merge.** Duplicate rows across runs happen.

If a coding agent writes your integration, the [Nimble plugin](https://docs.nimbleway.com/integrations/agent-skills/plugin-installation.md) generates this loop from the task description.

## Carry state between runs

Pass what you already have back in as `input_data` with an instruction: "exclude the domains in input\_data" for discovery, or the completed rows for an enrichment top-up. Do not paste lists into the prompt. `previous_interaction_id` continues a research thread on one entity; it is not a way to carry a dataset forward.

## What Jobs does today

[Extract Jobs](https://docs.nimbleway.com/nimble-sdk/agentic/jobs.md) schedule Extract Templates over an input list and deliver to S3, Databricks or a warehouse. They do not run Web Search Agents yet. For a scheduled agent job, run the loop above from your scheduler.

## Next Steps

> **[Effort and Cost](https://docs.nimbleway.com/nimble-sdk/web-search-agents/best-practices/effort-and-cost.md)**
>
> Size the job before you run it

> **[Evaluating Results](https://docs.nimbleway.com/nimble-sdk/web-search-agents/best-practices/evaluating-results.md)**
>
> Canaries and ground truth

> **[Rate Limits](https://docs.nimbleway.com/nimble-sdk/admin/rate-limits.md)**
>
> Account defaults and how to raise them


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.