Skip to main content
Batch Agents execute multiple agent instances in parallel, either returning the first successful result or collecting all results.

Overview

Run multiple agents concurrently with different strategies:
batch_agent.py
import asyncio

from notte_sdk import NotteClient
from notte_sdk.endpoints.agents import BatchRemoteAgent

client = NotteClient()


async def run_batch():
    with client.Session() as session:
        batch_agent = BatchRemoteAgent(session=session, max_steps=15, _client=client)

        # Run 3 agents in parallel, return first success
        result = await batch_agent.run(task="Find and extract pricing information", n_jobs=3, strategy="first_success")

        return result


result = asyncio.run(run_batch())
print(f"Success: {result.success}")
print(f"Answer: {result.answer}")

Why Use Batch Agents?

Improved Success Rate

Agents can fail due to:
  • Timing issues
  • Random page behavior
  • Rate limiting
  • Network issues
Running multiple attempts in parallel increases success probability:
success_rate_calc.py
# Single agent: 70% success rate
# 3 parallel agents: 97% success rate (1 - 0.3^3)

Faster Time-to-Success

Return as soon as any agent succeeds:
time_to_success.py
# Single agent: might take 60 seconds and fail
# 3 parallel agents: first success in ~20 seconds

Strategies

First Success

Return immediately when any agent succeeds:
first_success_strategy.py
import asyncio

from notte_sdk.agents import BatchRemoteAgent


async def main():
    batch_agent = BatchRemoteAgent(session=session, max_steps=10, _client=client)

    # Run 3 agents, return first success
    result = await batch_agent.run(
        task="Complete task",
        n_jobs=3,
        strategy="first_success",  # Default
    )

    print(result.success)  # True if any agent succeeded
    print(result.answer)  # Answer from successful agent


asyncio.run(main())
When to use:
  • Non-deterministic pages
  • Tasks prone to failures
  • Time-sensitive operations
  • You only need one successful result

All Finished

Wait for all agents to complete:
all_finished_strategy.py
import asyncio

from notte_sdk.agents import BatchRemoteAgent


async def main():
    batch_agent = BatchRemoteAgent(session=session, _client=client)

    # Run 5 agents, wait for all
    results = await batch_agent.run(task="Extract data", n_jobs=5, strategy="all_finished")

    # Returns list of results
    for i, result in enumerate(results):
        print(f"Agent {i + 1}: Success={result.success}")


asyncio.run(main())
When to use:
  • Comparing agent outputs
  • Gathering multiple perspectives
  • Testing different approaches
  • Statistical analysis

Configuration

Session Parameters

Batch agents create multiple sessions with the same configuration:
session_params.py
import asyncio

from notte_sdk import NotteClient
from notte_sdk.agents import BatchRemoteAgent

client = NotteClient()


async def main():
    with client.Session(headless=True, proxies="us", browser_type="chrome") as session:
        batch_agent = BatchRemoteAgent(
            session=session, reasoning_model="gemini/gemini-2.0-flash", max_steps=15, use_vision=True, _client=client
        )

        # All parallel agents use same session config
        result = await batch_agent.run(task="Task", n_jobs=3)


asyncio.run(main())

Number of Jobs

Control parallelism with n_jobs:
n_jobs.py
import asyncio

from notte_sdk.agents import BatchRemoteAgent


async def main():
    batch_agent = BatchRemoteAgent(session=session, _client=client)

    # Light parallelism (2-3 agents)
    result = await batch_agent.run(task="Task", n_jobs=2)

    # Medium parallelism (3-5 agents)
    result = await batch_agent.run(task="Task", n_jobs=5)

    # Heavy parallelism (5-10 agents)
    result = await batch_agent.run(task="Task", n_jobs=10)


asyncio.run(main())
Cost consideration: Each agent incurs full costs, so n_jobs=5 costs 5x a single agent.

Agent Parameters

Pass any agent parameter:
agent_params.py
from notte_sdk.agents import BatchRemoteAgent

batch_agent = BatchRemoteAgent(
    session=session,
    reasoning_model="anthropic/claude-3.5-sonnet",
    max_steps=20,
    use_vision=True,
    vault=vault,
    persona=persona,
    _client=client,
)

Use Cases

1. Unreliable Pages

Pages with random failures:
unreliable_pages.py
import asyncio

from notte_sdk import NotteClient
from notte_sdk.agents import BatchRemoteAgent

client = NotteClient()


async def main():
    # Page sometimes has timing issues
    with client.Session() as session:
        batch_agent = BatchRemoteAgent(session=session, max_steps=10, _client=client)

        # Run 3 attempts
        result = await batch_agent.run(
            task="Click the submit button that appears after 2 seconds", n_jobs=3, strategy="first_success"
        )

        # Much higher success rate than single agent


asyncio.run(main())

2. Rate-Limited Sites

Avoid rate limit failures:
rate_limited_sites.py
import asyncio

from notte_sdk import NotteClient
from notte_sdk.agents import BatchRemoteAgent

client = NotteClient()


async def main():
    # Some agents might get rate limited
    with client.Session(proxies="residential") as session:
        batch_agent = BatchRemoteAgent(session=session, max_steps=15, _client=client)

        # Run 5 agents from different IPs
        result = await batch_agent.run(task="Extract product data", n_jobs=5, strategy="first_success")


asyncio.run(main())

3. A/B Testing

Test which model works best:
ab_testing.py
import asyncio

from notte_sdk.agents import BatchRemoteAgent


async def main():
    # Test different models
    models = ["gemini/gemini-2.0-flash", "anthropic/claude-3.5-sonnet", "openai/gpt-4o"]

    results = []
    for model in models:
        batch_agent = BatchRemoteAgent(session=session, reasoning_model=model, _client=client)

        result = await batch_agent.run(task="Complex task", n_jobs=3, strategy="first_success")

        results.append({"model": model, "success": result.success, "steps": len(result.steps)})

    # Compare which model performed best


asyncio.run(main())

4. Consensus Results

Get multiple agent opinions:
consensus_results.py
import asyncio

from notte_sdk.agents import BatchRemoteAgent


async def main():
    batch_agent = BatchRemoteAgent(session=session, max_steps=10, _client=client)

    # Get results from all agents
    results = await batch_agent.run(task="Extract the company's revenue", n_jobs=5, strategy="all_finished")

    # Find consensus
    revenues = [r.answer for r in results if r.success]
    consensus = max(set(revenues), key=revenues.count)


asyncio.run(main())

Performance

Execution Time

With first_success strategy:
execution_time.py
# Single agent: 40 seconds average
# Batch of 3: ~13 seconds (first success)
# Speedup: ~3x faster for successful case

Cost

Batch agents cost more:
cost.py
# Single agent: $0.02
# Batch of 3: $0.06 (3x cost)
# Batch of 5: $0.10 (5x cost)

# But: Higher success rate means fewer retries

Success Rate

Probability of at least one success:
Single Agent Success2 Parallel3 Parallel5 Parallel
50%75%87.5%96.9%
70%91%97.3%99.8%
80%96%99.2%99.97%

Best Practices

1. Start with 2-3 Parallel Agents

Balance cost and reliability:
start_with_parallel.py
import asyncio

from notte_sdk.agents import BatchRemoteAgent


async def main():
    batch_agent = BatchRemoteAgent(session=session, _client=client)

    # Good starting point
    result = await batch_agent.run(
        task="Task with ~70% success rate",
        n_jobs=3,  # 97% combined success
        strategy="first_success",
    )


asyncio.run(main())

2. Use for Critical Tasks

Worth the cost for important operations:
critical_tasks.py
import asyncio

from notte_sdk import NotteClient
from notte_sdk.agents import BatchRemoteAgent

client = NotteClient()


async def main():
    # Critical: Use batch
    with client.Session() as session:
        batch_agent = BatchRemoteAgent(session=session, _client=client)
        result = await batch_agent.run(task="Process payment", n_jobs=3)

        # Non-critical: Use single agent
        agent = client.Agent(session=session)
        result = agent.run(task="Nice-to-have data")


asyncio.run(main())

3. Monitor Success Rates

Track if batch execution is needed:
monitor_success_rates.py
import asyncio

from notte_sdk.agents import BatchRemoteAgent


async def main():
    # Test single agent success rate
    successes = 0
    for _ in range(10):
        result = agent.run(task="Test task")
        if result.success:
            successes += 1

    success_rate = successes / 10

    if success_rate < 0.8:
        # Use batch for low success rates
        batch_agent = BatchRemoteAgent(session=session, _client=client)
        result = await batch_agent.run(task="Test task", n_jobs=3)


asyncio.run(main())

4. Use Appropriate Strategy

Choose based on your needs:
appropriate_strategy.py
# Need one good result quickly
strategy = "first_success"

# Need to compare multiple results
strategy = "all_finished"

# Need statistical confidence
strategy = "all_finished"

5. Set Reasonable Timeouts

Prevent hanging:
timeout.py
import asyncio

from notte_sdk.agents import BatchRemoteAgent


async def run_with_timeout():
    batch_agent = BatchRemoteAgent(session=session, _client=client)

    try:
        result = await asyncio.wait_for(
            batch_agent.run(task="Task", n_jobs=3),
            timeout=120,  # 2 minute timeout
        )
        return result
    except asyncio.TimeoutError:
        print("Batch execution timed out")
        raise


asyncio.run(run_with_timeout())

Limitations

Resource Usage

Batch agents consume more resources:
  • Sessions: Creates n separate sessions
  • Browsers: Opens n browser instances
  • Memory: n times single agent memory
  • API calls: n times single agent calls

Concurrency Limits

Account for rate limits:
concurrency_limits.py
import asyncio

from notte_sdk.agents import BatchRemoteAgent


async def main():
    batch_agent = BatchRemoteAgent(session=session, _client=client)

    # May hit account concurrency limit
    result = await batch_agent.run(
        task="Task",
        n_jobs=20,  # Might exceed concurrent session limit
    )

    # Safer approach
    result = await batch_agent.run(
        task="Task",
        n_jobs=5,  # Within typical limits
    )


asyncio.run(main())

Cost Scaling

Costs multiply with n_jobs:
cost_scaling.py
# Single agent task: $0.02
# n_jobs=3: $0.06
# n_jobs=10: $0.20

# Use batch only when success rate justifies cost

Async Only

Batch agents require async execution:
async_only.py
import asyncio

from notte_sdk.agents import BatchRemoteAgent


async def run_batch():
    batch_agent = BatchRemoteAgent(session=session, _client=client)
    result = await batch_agent.run(task="Task", n_jobs=3)
    return result


# Must use asyncio.run
result = asyncio.run(run_batch())

Next Steps