Quickstart
Get a browser-agent run on Banana Peel in a few minutes — no separate provider accounts required to start. The recommended workflow is two calls to POST /api/v1/responses: calibrate with routing: "learning" on your real task first, then run production on routing: "smart".
Prerequisites
- Agents: if there is no key yet, run
npx -y @banana-peel/cli init --agent --json— see For AI agents and Agent-native signup. Do not ask a human to create an account. - Humans who already have an account can manage keys at /keys (optional advanced path).
BANANA_PEEL_API_KEY(aliasBANANAPEEL_API_KEY) and optionalBANANA_PEEL_BASE(defaults tohttps://bananapeel.com)
The flow: Learning first, then Smart
- Calibrate with Learning. Run your real task with
routing: "learning"(orlearning-allonce for the full-catalog sweep) — the routing brain tests candidate runners against your site and task and stores verified evidence of what actually works there. - Verify. Learning creates always return
202; poll untilcompleted, sanity-check the output, and read the winners inbanana_peel.learning_ranking. - Ship on Smart. Switch the same request to
routing: "smart"— it exploits the calibrated evidence (first pick, cheapest proven path, learned fallbacks), and every subsequent run keeps teaching it.
Smart works out of the box — cold-start exploration, site profiling, and widening handle never-seen sites. Learning-first is the recommended way to get the best first-run reliability on a specific site, not a requirement. If you just want a result right now, skip straight to step 3 — but calibrate before you point real traffic at a site that matters.
1. Calibrate with Learning
Learning and learning-all exist to teach the router the best path: they calibrate the routing brain on a domain/task so later smart runs pick the winning runner. A Learning run needs a resolvable target URL — pass url (or include an https:// URL in the task text) or the create is rejected with 400 missing_url before anything bills. Be deliberate about when you run it: Learning runs are paid calibration sweeps: every executed attempt bills (not success-only), $20 default spend ceiling (set max_cost_usd to lower it), and at most 3 Learning-family runs per account in flight at once (learning and learning-all share the cap; creates beyond it are refused with 429 rate_limited + Retry-After — nothing created, nothing billed). Full mechanics: Learning mode.
export BANANA_PEEL_BASE=https://bananapeel.com
export BANANA_PEEL_API_KEY=bp_live_…
# 1) Calibrate — Learning always returns 202 and runs in the background
curl -s "$BANANA_PEEL_BASE/api/v1/responses" \
-H "Authorization: Bearer $BANANA_PEEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "banana-peel",
"input": "Extract the title from https://example.com",
"url": "https://example.com",
"routing": "learning"
}'
# → { "id": "resp_…", "status": "in_progress", … }
# 2) Poll until terminal — winners land in banana_peel.learning_ranking
curl -s "$BANANA_PEEL_BASE/api/v1/responses/resp_…" \
-H "Authorization: Bearer $BANANA_PEEL_API_KEY"2. Verify what Learning found
While the run is in flight, banana_peel.progress shows attempts and spend to date. On the completed object, banana_peel.learning_ranking lists the winners (fastest + cheapest first) that future smart runs on this domain will prefer, and banana_peel.routing_attempts is the full per-runner attempt table. Sanity-check output_text — a genuine success on your real task is exactly the evidence smart routing will exploit.
{
"id": "resp_…",
"object": "response",
"status": "completed",
"model": "banana-peel",
"output_text": "{ \"title\": \"Example Domain\", … }",
"banana_peel": {
"routing_strategy": "learning",
"learning_ranking": ["steel", "browserbase", "hyperbrowser"],
"learning": {
"mode": "learning",
"batch_size": 9,
"bills_every_attempt": true,
"max_cost_usd": 20
},
"routing_attempts": [
{ "runner": "steel", "status": "completed", "cost_usd": 0.04, "timing_ms": 3900 },
{ "runner": "browserbase", "status": "completed", "cost_usd": 0.06, "timing_ms": 4600 }
]
}
}3. Run production on Smart
Switch the same request to routing: "smart" for everyday traffic. Smart ranks runners by live success evidence for your domain — a prior Learning ranking is exactly the evidence it prefers — picks the winner first, falls back on failure, and keeps learning from every outcome. See Smart routing.
export BANANA_PEEL_BASE=https://bananapeel.com
export BANANA_PEEL_API_KEY=bp_live_…
curl -s "$BANANA_PEEL_BASE/api/v1/responses" \
-H "Authorization: Bearer $BANANA_PEEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "banana-peel",
"input": "Extract the title from https://example.com",
"routing": "smart"
}'4. Read the result
Successful runs return a Responses-style object. Banana Peel adds detail under banana_peel: the runner that executed (banana_peel.runner), the ranked chain, timing, cost, links to the browser session and replay, and any fallbacks.
{
"id": "resp_…",
"object": "response",
"status": "completed",
"model": "banana-peel",
"output_text": "{ \"title\": \"Example Domain\", … }",
"banana_peel": {
"routing": "smart",
"routing_note": "smart routing for example.com — ranked by live success history",
"ranked_runners": ["browserbase", "steel", "hyperbrowser", "…"],
"runner": "browserbase",
"timing_ms": 4200,
"artifacts": [],
"fallback_from": null
}
}Optional: Output normalization (+$0.10)
Need structured JSON? Pass normalize: true with an output_schema (or OpenAI text.format / response_format). Adds a flat $0.10 on that run. Build schemas in Console → Normalize; full request example in Create a response.
What to remember
- Call
/api/v1/responsesfor new integrations — it's the Banana Peel API. - The recommended flow is Learning → Smart: calibrate with
routing: "learning"on your real task (learning-allonce for the full-catalog sweep), verifybanana_peel.learning_ranking, then run production onsmart— it exploits the calibrated evidence and keeps learning from every run. Smart also works out of the box on never-seen sites.routingcan also pin a named runner (browserbase, steel, skyvern, …), a compliance pool (SOC2…), or an ordered chain. - Every runner in the catalog competes on the same metrics — pick one, or let smart routing choose.
- Output normalization (+$0.10) is optional — only when you need a fixed JSON shape.
- When a target site asks for MFA, 2FA, or an OTP, the run pauses immediately and asks the user for the code. It does not keep trying other runners. Submit the code with POST /api/v1/responses/{id}/input { "code": "123456" } (or { "answer": "…" } for text/confirm). MCP: submit_input({ id, code }). Then keep polling until completed or failed. CAPTCHA and other bot-detection gates (reCAPTCHA, hCaptcha, Turnstile, DataDome, Cloudflare challenge, “prove you are human”) are solved, re-routed, or end blocked with reason bot_challenge. They never become a human text prompt. Full guide: MFA / OTP.
- Have an existing provider SDK? Use a compatibility wrapper to keep your client and still get routing.
Next steps
- For AI agents — OpenAPI, llms.txt, skill, checklist
- MFA / OTP — pause, poll, submit; CAPTCHA is not MFA
- MCP server — Cursor / Claude Desktop native tools
- Browser Agent API
- Routing
- Migrating from Browserbase / Steel / …