browser_ session
一个浏览器页面,跨多次 API 调用由你保留。打开会话,用 snapshot 看清页面提供了什么,对该 snapshot 返回的引用执行操作,再看一次——登录只需付一次费用,而不是每次调用都付一次。
使用场景
登录一次,读取多页
在第一次调用中登录,随后在会话存活期间持续导航并读取登录墙后的内容,无需每次重复登录
探索陌生的应用
对从未见过的页面执行 snapshot,从响应中读出它的可交互元素,在下一次调用中直接对它们操作,而不是猜测 CSS 选择器
智能体循环
为智能体提供一个「先看后做」的循环:snapshot 树是观察,引用是动作空间,下一次 snapshot 就是反馈
多步骤向导
每次调用推进结账或开户流程的一步,先确认页面变成了什么样,再决定下一个动作
调试失败的操作链
当一次性操作链因猜错选择器而失败时,打开一个会话逐步执行,并在步骤之间截图
交互之后再读取
从实时 DOM 中提取 markdown、HTML、文本或元数据,反映会话点击过一切之后的页面状态
Endpoint
/api/v1/tools/browser_sessionParameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
operation | string | Required | - | 本次调用执行会话中的哪一步:`open`、`snapshot`、`act`、`read`、`screenshot`、`close` 或 `list`。除 `open` 和 `list` 之外的每个操作都需要 `session_id`。操作同时决定价格——参见下方的**操作及其费用**。 Example: snapshot |
session_id | string | Optional | - | `operation: "open"` 返回的 id,在该会话后续的每次调用中携带。`snapshot`、`act`、`read`、`screenshot` 和 `close` 都需要它。未知、已过期、已关闭或属于其他账户的 id 会得到完全相同的「Session not found」——id 不可枚举,因此错误绝不会告诉你命中的是哪一种情况。 Example: 9f2c1e6a-4b7d-4c3e-8a5f-1d2e3f4a5b6c |
url | string | Optional | - | 会话打开时加载的页面。`open` 必须提供,其他操作会忽略它——会话建立之后,用 `act` 中的 `navigate` 操作让它移动。该 URL 会在启动任何浏览器之前通过 SSRF 检查和目标站点的 robots.txt,会话内的每次导航同样如此。 Example: https://app.example.com/login |
stealth | boolean | Optional | false | 仅用于 `open`。使用隐身 Chromium 引擎的 `medium` 配置运行会话,而不使用标准浏览器池。它启动更慢,并且只是渲染 JavaScript,并不解决挑战。会话在其整个生命周期内都保持该设置。 Example: false |
ttl | number | Optional | 600 | 仅用于 `open`。会话最多可以存活多久,以秒计(30-3600)。无论你正在做什么,会话都会按这个时钟关闭,因此请按计划的工作量来设定。 Example: 600 |
activity_ttl | number | Optional | 300 | 仅用于 `open`。会话在两次调用之间可以闲置多久,以秒计(10-3600)。每个操作都会重置这个时钟;两个时钟中先到期的那个会关闭会话并释放其页面。 Example: 300 |
viewport | object | Optional | - | 仅用于 `open`。会话运行所在的浏览器窗口:`{ width, height }`,宽 800-1920,高 600-1080。省略时使用浏览器池自身的尺寸。 Example: {"width": 1440, "height": 900} |
timeout | number | Optional | 30000 | 本次调用中浏览器工作的时间预算,以毫秒计(10000-120000):`open` 时为首次导航,`act` 时为整条操作链。请保持在约 25 秒的 REST 窗口之内。 Example: 30000 |
respect_robots | boolean | Optional | true | 遵守目标站点的 robots.txt。省略该参数时按合规默认值(`true`)执行:robots.txt 对 `CrawlForge` 禁止的 URL 会在打开浏览器之前被拒绝,此端点不会为此扣费,`act` 中的每个 `navigate` 操作也会同样检查。它只作用于所在的这一次调用,会话绝不会记住它,因此每次覆盖都必须像第一次那样明确地重新声明。仅在您与目标站点另有约定时才设为 `false`——该覆盖会记录到您的 API key 上。 Example: true |
interactive_only | boolean | Optional | true | 仅用于 `snapshot`。只列出接受指针或键盘的元素,也就是会获得引用的那些。设为 false 可一并列出标题和地标元素:这有助于了解页面结构,但那些节点永远不会获得引用。 Example: true |
max_nodes | number | Optional | 200 | 仅用于 `snapshot`。限制树中列出的节点数量(1-1000)——在 `interactive_only` 的默认设置下,每个节点各对应一个引用。当该上限中断遍历时,结果会报告 `truncated: true`。 Example: 200 |
actions | array | Optional | - | 仅用于 `act`。针对会话已持有的页面运行 1-20 个浏览器操作,词汇与 [scrape_with_actions](/docs/api-reference/tools/scrape-with-actions) 完全相同——`wait`、`click`、`type`、`press`、`scroll`、`screenshot`、`select`、`hover`、`navigate`、`snapshot`——以及各自与类型相关的字段。把 `selector` 指向会话上一次 snapshot 得到的 `@e1` 引用,而不是猜测的 CSS 选择器。托管 API 会拒绝 `executeJavaScript`:该脚本将运行在我们基础设施上的浏览器中,而不是你的机器上。 Example: [{"type": "type", "selector": "@e1", "text": "user@example.com"}, {"type": "click", "selector": "@e3"}] |
continue_on_error | boolean | Optional | false | 仅用于 `act`。当某个操作失败时继续执行其余操作,而不是中止整条链。每个操作的结果会说明哪些实际执行了。 Example: false |
formats | array | Optional | ["markdown"] | 仅用于 `read`。从页面当前状态中提取什么:`markdown`、`html`、`text`、`json`(标题、元数据和结构化数据)。内容来自实时 DOM,因此反映会话的 cookie 以及它点击过的一切,而不是对该 URL 的重新抓取。 Example: ["markdown", "json"] |
full_page | boolean | Optional | false | 仅用于 `screenshot`。捕获整个可滚动页面,而不仅是可视区域。 Example: false |
format | string | Optional | png | 仅用于 `screenshot`。图像格式:`png` 或 `jpeg`。 Example: png |
quality | number | Optional | 80 | 仅用于 `screenshot`。JPEG 质量,0-100。PNG 会忽略该值。 Example: 80 |
selector | string | Optional | - | 仅用于 `screenshot`。只捕获一个元素而非整页:可以是 CSS 选择器,也可以是会话上一次 snapshot 得到的 `@e1` 引用。 Example: @e2 |
max_inline_chars | number | Optional | 40000 | 仅适用于 `snapshot`、`act` 和 `read`——这三个返回页面内容的操作。内联返回的最大结果大小,以其 JSON 的字符数计(1,000-10,000,000)。超过后,响应会携带 `preview`、`result_handle`、`total_chars`、`truncated: true` 和 `expires_at`,其余部分由 [read_result](/docs/api-reference/tools/read-result) 读取,每次调用 1 credit;参与计算的字段为 `content.markdown`、`content.text`、`content.html` 和 `snapshot.tree`。已存储的结果保留 1 小时。会话本身不受影响:页面仍然打开着,下一个操作依然能看到完整内容。 Example: 40000 |
redact_pii | boolean | object | Optional | false | 仅适用于 `snapshot`、`act` 和 `read`;其余操作返回的是记账信息,不会被处理。在结果被存储或返回之前,从本次调用返回的文本中移除个人数据——已登录页面的快照 `tree` 和抽取内容往往带有账户持有人的信息,这正是该参数的用武之地。`true` 是 `{ mode: "fast" }` 的简写:四个正则类别全开,以标签替换。只要请求了脱敏,响应就会在 `data` 内带上 `redaction: { entities, count, mode }`,即使没有任何匹配(`count: 0`)也会带上,因此“什么都没找到”永远不会被误认为“参数被忽略了”。脱敏在结果存储**之前**执行,因此超出阈值、之后再用 [read_result](/docs/api-reference/tools/read-result) 读回的结果本身就已脱敏。两条刻意的边界:地址类字段(`url`、`link`、`href`、`canonical_url`)从不脱敏;由文本派生的计数(`content_length`、`word_count`、`character_count`)描述的是抽取时的文本,即脱敏之前的文本。 Example: true |
操作及其费用
每次调用都按各自的 operation 计费,因此一次便宜的查看不会为昂贵的 open 买单。没有任何操作是免费的:close 和 list 仍各需 1 credit。
@e1、@e2、…。这是循环中「看」的那一半,也是下一次调用要瞄准的目标。scrape 同价,因为这是同样的内容提取工作。crawlforge://screenshot/{id} 资源 URI 的形式返回,而不是内联 base64,并且此 REST 端点会原样透传该 URI 而不解析它——因此目前只能通过 CrawlForge MCP server 读取其字节。一个会话能活多久,你又能同时拥有几个
会话存在于执行后端某一个实例的内存中。 它无法在重新部署或实例重启后存活,因此请把 sessionId 当作短命之物,并准备好下一次调用返回「Session not found」——重新打开一个继续就是了。会话按设计就是短命的:ttl(默认 600s,范围 30-3600)是绝对时钟,activity_ttl(默认 300s,范围 10-3600)是闲置时钟,先到期的那个会关闭会话。一个 REST API key 同一时间只能持有一个会话。 在一个会话仍然存活时再次 open 会被一个具名错误拒绝,而不是排队等待——会话名额要等好几分钟后才随 TTL 释放,排队只会让调用一直挂着。托管后端在所有客户之间总共只保留三个会话,这正是每个 key 限一个的原因:用完就 close,而不是让 TTL 自己跑完,下一次打开的机会就是你的。
通过引用定位元素
snapshot 会为它找到的每个可交互元素附上稳定的引用,而只要会话保留着页面,它就保留着这些引用——因此整个循环是:打开、snapshot、在另一次调用中对 @e1 / @e2 执行操作、再次 snapshot。这正是会话的意义所在:一次性操作链必须在看到页面之前就写死选择器。引用仅对拍摄它的那个文档有效:任何导航——navigate 操作,或加载新页面的点击——都会使全部引用失效,使用失效的引用会以错误明确失败并提示你重新执行 snapshot,而绝不会悄悄点错东西。
该用 browser_session 还是 scrape_with_actions?
两者都驱动真实浏览器,并接受相同的操作词汇。区别在于你是否已经知道页面长什么样。
open、snapshot、act、act、read、close——需要 9 credits,而一次性操作链是 5 credits,但后者因猜错选择器而失败的可能性要大得多。这里没有持久化登录
会话的 cookie 和 localStorage 与会话同生共死。此 API 没有任何 profile 参数,也无法把已登录的浏览器保存下来供下次使用,因此每打开一个新会话都必须重新登录。保存登录配置文件是后续的、仅限本地的功能——请不要据此规划托管环境下的工作流。
请求示例
# 1. Open the session (3 credits). The id comes back as data.sessionId.
curl -X POST https://crawlforge.dev/api/v1/tools/browser_session \
-H "X-API-Key: cf_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"operation": "open",
"url": "https://app.example.com/login",
"ttl": 600
}'
# {"success": true, "data": {"sessionId": "9f2c1e6a-4b7d-4c3e-8a5f-1d2e3f4a5b6c", ...}}
# 2. Look at the page before touching it (1 credit).
curl -X POST https://crawlforge.dev/api/v1/tools/browser_session \
-H "X-API-Key: cf_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"operation": "snapshot",
"session_id": "9f2c1e6a-4b7d-4c3e-8a5f-1d2e3f4a5b6c"
}'
# data.snapshot.tree comes back as:
# [document] "Sign in"
# @e1 [textbox] "Email"
# @e2 [textbox] "Password"
# @e3 [button] "Sign in"
# 3. Act on those refs in a SEPARATE call (1 credit). Same page, same cookies.
curl -X POST https://crawlforge.dev/api/v1/tools/browser_session \
-H "X-API-Key: cf_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"operation": "act",
"session_id": "9f2c1e6a-4b7d-4c3e-8a5f-1d2e3f4a5b6c",
"actions": [
{"type": "type", "selector": "@e1", "text": "user@example.com"},
{"type": "type", "selector": "@e2", "text": "secret123"},
{"type": "click", "selector": "@e3"},
{"type": "wait", "duration": 1000}
]
}'
# 4. Read the page the login landed on (2 credits).
curl -X POST https://crawlforge.dev/api/v1/tools/browser_session \
-H "X-API-Key: cf_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"operation": "read",
"session_id": "9f2c1e6a-4b7d-4c3e-8a5f-1d2e3f4a5b6c",
"formats": ["markdown"]
}'
# 5. Close it rather than waiting for the TTL (1 credit). 8 credits in all.
curl -X POST https://crawlforge.dev/api/v1/tools/browser_session \
-H "X-API-Key: cf_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"operation": "close",
"session_id": "9f2c1e6a-4b7d-4c3e-8a5f-1d2e3f4a5b6c"
}'响应示例
{ "success": true, "data": { "success": true, "operation": "snapshot", "sessionId": "9f2c1e6a-4b7d-4c3e-8a5f-1d2e3f4a5b6c", "url": "https://app.example.com/login", "stealth": false, "expiresAt": 1789459200000, "idleExpiresAt": 1789458900000, "snapshot": { "snapshotId": "a3f19c2d", "url": "https://app.example.com/login", "title": "Sign in", "tree": "[document] \"Sign in\"\n @e1 [textbox] \"Email\"\n @e2 [textbox] \"Password\"\n @e3 [button] \"Sign in\"", "refCount": 3, "nodeCount": 3, "truncated": false, "interactiveOnly": true } }, "credits_used": 1, "credits_remaining": 996, "processing_time": 180}data.operation本次调用所执行的操作——每个响应都会原样回显它data.sessionId在该会话后续的每次调用中都要发送它data.url会话的页面此刻所在的位置,导航会改变它data.expiresAt绝对时钟 `ttl` 的到期时间,以 Unix 纪元以来的毫秒数表示data.idleExpiresAt闲置时钟 `activity_ttl` 的到期时间;每个操作都会把它往后推data.snapshot.tree无障碍树。每个可交互元素都带有你在下一次调用中要瞄准的引用data.snapshot.refCount本次 snapshot 中有多少个元素获得了引用data.snapshot.truncated当 `max_nodes` 在页面结束前中断遍历时为 truecredits_used本次调用扣除的 credits——snapshot 为 1,open 为 3processing_time仅本次调用的耗时(毫秒),而非整个会话错误处理
会话未找到(502 TOOL_ERROR)
session_id 未知、已过期、已关闭,或属于其他账户——这四种情况回应完全一致,因此无法从外部探测 id。会话同样无法在执行后端重启后存活。打开一个新会话继续即可;失败的调用不计费。
已达会话上限(502 TOOL_ERROR)
您已持有上限的一个打开会话。该调用会被拒绝而不是排队,因为会话名额要等好几分钟后才随 TTL 释放。对已有的会话发送 operation: "close"——operation: "list" 会告诉你它的 id——或者等待它的 TTL。
浏览器运行时未配置(503 TOOL_NOT_AVAILABLE)
该工具需要浏览器自动化运行时。当托管执行后端未配置时,调用会立即返回 503,且不扣除 credits。
执行后端超时(504 MCP_UPSTREAM_TIMEOUT)
导航或操作链超出了执行后端的时间预算。请缩短等待、把整条链拆成两次 act 调用(会话仍然在那里),或稍后重试。失败不计费。
被 robots.txt 阻止(502 TOOL_ERROR)
目标站点的 robots.txt 对 CrawlForge 禁止了该 URL,因此没有启动任何浏览器,也没有产生任何费用。act 中的每个 navigate 操作都会同样检查,而不仅是你打开时用的 URL。若您与目标站点另有约定,可设置 respect_robots: false 予以覆盖——该覆盖会记录到您的 API key 上。
请求无效(400 Bad Request)
该操作缺少它所需的参数——open 没有 url、act 没有 actions,或除 open 和 list 之外的任何操作没有 session_id——又或者某个动作未通过它自己的模式校验。不会产生任何费用。
Credits 不足(402 Payment Required)
您的账户 credits 不足以支付本次操作(最多 3 个)。购买更多 credits 或 升级您的套餐。
超出速率限制(429 Too Many Requests)
您已超出套餐的速率限制。请稍候片刻,或 升级您的套餐 以获得更高限额。
在任何会改变页面的操作之后都重新 snapshot,而不只是在开头:会导航的点击会使全部引用失效,而新的树只需 1 credit。当一个会话只会执行一条固定的链时,scrape_with_actions 更便宜,而且只需一次往返。
Credit 费用
operation 计费,而不是按工具计费:open 3、read 2,snapshot、act、screenshot、close 和 list 各 1。3 credits 是上限——即 open 的价格,也是无法识别的操作所收取的价格。一个登录后读取的流程(open、snapshot、act、act、read、close)合计 9 credits。Free 套餐: 1,000 个一次性试用 credits = 约 110 次登录读取会话
Hobby 套餐: 5,000 credits/月 = 约 550 次会话($19/mo)
Professional 套餐: 50,000 credits/月 = 约 5,500 次会话($99/mo)
Business 套餐: 250,000 credits/月 = 约 27,000 次会话($399/mo)
相关工具
准备好试用 browser_session 了吗?免费注册,获取 1,000 credits,开始构建。