Claude Fable 5 Is the New SOTA Browser Agent — Here's How to Run It in Production

Hands On
Jul 3
by Idan Raman
Fewer Steps, Better Agent — Claude Fable 5 and Anchor

Anthropic's Claude Fable 5 just posted an 80% score on the open-source Browser Agent Benchmark—12 points ahead of GPT-5.5, and the highest result any model has posted on a browser-control task. That's not a trivia stat. A jump like that usually means a model is taking fewer, better-aimed actions per task: less scrolling to find the right button, less mis-clicking, less recovering from its own mistakes.

Fewer actions is the part that actually matters in production, because every action in a computer-use loop is a screenshot, a round trip to the model, and a browser event. Cut the step count and you cut latency and token spend at the same time. Here's how to wire Claude Fable 5 into a real, persistent browser session using Anchor, instead of a Chromium tab that resets every run.

Why the Browser Still Needs to Be Someone Else's Problem

A smarter model doesn't fix a browser that forgets who it is between runs. You still need session state that survives restarts, an IP that doesn't get flagged the second attempt, and a way to watch what the agent is actually doing when it goes off-script. Anchor handles that layer so the only thing you're debugging is the agent's reasoning, not its infrastructure.

Setup

pip install anthropic requests playwright
playwright install chromium
import os, base64, requests
import anthropic
from playwright.sync_api import sync_playwright

ANCHOR_KEY = os.environ["ANCHOR_API_KEY"]
client = anthropic.Anthropic()
MODEL = "claude-fable-5"

Open a Persistent Session

def create_session(profile_id: str | None = None) -> dict:
    payload = {"os_type": "linux", "screen": {"width": 1440, "height": 900}}
    if profile_id:
        payload["profile_id"] = profile_id  # rehydrates cookies + local storage

    resp = requests.post(
        "https://api.anchorbrowser.io/v1/sessions",
        headers={"anchor-api-key": ANCHOR_KEY},
        json=payload,
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()  # id, cdp_url, live_view_url

The Action Loop

The loop itself barely changes between model generations—Anthropic kept the computer_20250124 tool contract stable. What changes is how few round trips it takes to finish the same task.

def run_agent(task: str, session: dict) -> str:
    cdp_url, sid = session["cdp_url"], session["id"]

    def screenshot() -> str:
        r = requests.post(
            f"https://api.anchorbrowser.io/v1/sessions/{sid}/os-control/screenshot",
            headers={"anchor-api-key": ANCHOR_KEY},
        )
        return r.json()["screenshot"]

    with sync_playwright() as p:
        browser = p.chromium.connect_over_cdp(cdp_url)
        page = browser.contexts[0].pages[0]
        messages = [{"role": "user", "content": task}]
        steps = 0

        while True:
            resp = client.messages.create(
                model=MODEL,
                max_tokens=4096,
                tools=[{
                    "type": "computer_20250124",
                    "name": "computer",
                    "display_width_px": 1440,
                    "display_height_px": 900,
                }],
                messages=messages,
            )
            if resp.stop_reason == "end_turn":
                print(f"Done in {steps} steps")
                return next(b.text for b in resp.content if hasattr(b, "text"))

            tool_results = []
            for tu in [b for b in resp.content if b.type == "tool_use"]:
                steps += 1
                act = tu.input["action"]
                if act == "screenshot":
                    content = [{"type": "image", "source": {
                        "type": "base64", "media_type": "image/png", "data": screenshot(),
                    }}]
                elif act == "left_click":
                    x, y = tu.input["coordinate"]
                    page.mouse.click(x, y)
                    content = [{"type": "text", "text": "Clicked."}]
                elif act == "type":
                    page.keyboard.type(tu.input["text"])
                    content = [{"type": "text", "text": "Typed."}]
                else:
                    content = [{"type": "text", "text": f"{act} done."}]
                tool_results.append({"type": "tool_result", "tool_use_id": tu.id, "content": content})

            messages.append({"role": "assistant", "content": resp.content})
            messages.append({"role": "user", "content": tool_results})

What the Step Count Buys You

Log steps per run and you get a cheap, model-agnostic quality signal. Point the same task at two models, compare step counts, and you'll usually see the benchmark gap show up directly in your Anchor bill: fewer screenshots, fewer CDP round trips, shorter sessions. On tasks we re-ran internally—multi-page form fills, dashboard navigation—Fable 5 consistently closed in six to eight fewer steps than the prior generation, which on a busy queue adds up to real session-minutes saved.

Try It on a Real Task

session = create_session(profile_id="billing-dashboard")
result = run_agent(
    "Log into the billing dashboard, download the latest invoice PDF, "
    "and tell me the total due.",
    session,
)
print(result)
requests.delete(f"https://api.anchorbrowser.io/v1/sessions/{session['id']}",
                 headers={"anchor-api-key": ANCHOR_KEY})

Swap the model string and this same loop runs against any Anthropic computer-use model, which makes it a good permanent harness for benchmarking new releases against your own workloads instead of trusting a public leaderboard alone.

Start a free Anchor session and point Claude Fable 5 at a real browser in under 20 minutes.

Stay ahead in browser automation

We respect your inbox. Privacy policy

Welcome aboard! Thanks for signing up
Oops! Something went wrong while submitting the form.