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

# 抓取后交互

> 通过 prompt 或运行代码与获取的页面交互。

抓取页面以获取干净的数据，然后调用 `/interact` 在该页面中开始执行 actions：点击按钮、填写表单、提取动态内容，或进一步深入导航。只需描述你想做什么；如果需要完全控制，也可以编写代码。

<CardGroup cols={3}>
  <Card title="AI prompts" icon="wand-magic-sparkles">
    描述你希望在页面中执行的操作
  </Card>

  <Card title="代码执行" icon="code">
    通过代码安全地与 playwright、agent-browser 交互
  </Card>

  <Card title="实时视图" icon="eye">
    通过可嵌入的流实时观看或与浏览器交互
  </Card>
</CardGroup>

<div id="how-it-works">
  ## 工作原理
</div>

1. 使用 `POST /v2/scrape` **抓取**一个 URL。响应会在 `data.metadata.scrapeId` 中返回 `scrapeId`。如果你想持久保存浏览器状态，请在此请求中传入 `profile`。
2. 调用 `POST /v2/scrape/{scrapeId}/interact`，并传入 `prompt` 或 Playwright `code` 进行**交互**。此处不要传入 `profile`；交互会话会继承抓取任务中的 `profile`。
3. 完成后，使用 `DELETE /v2/scrape/{scrapeId}/interact` **停止**该会话。对于可写的 `profile`，会话停止时会保存更改。

<div id="quick-start">
  ## 快速开始
</div>

抓取页面、与其交互，然后停止会话：

<CodeGroup>
  ```python Python theme={null}
  from firecrawl import Firecrawl

  app = Firecrawl(
    # 无需 API 密钥即可开始使用 — 添加一个以获得更高限流：
    # api_key="fc-YOUR-API-KEY",
  )

  # 1. 抓取 Amazon 首页
  result = app.scrape("https://www.amazon.com", formats=["markdown"])
  scrape_id = result.metadata.scrape_id

  # 2. 交互 — 搜索商品并获取价格
  app.interact(scrape_id, prompt="Search for iPhone 16 Pro Max")
  response = app.interact(scrape_id, prompt="Click on the first result and tell me the price")
  print(response.output)

  # 3. 停止会话
  app.stop_interaction(scrape_id)
  ```

  ```js Node theme={null}
  import { Firecrawl } from 'firecrawl';

  const app = new Firecrawl({
    // 无需 API 密钥即可开始使用 — 添加一个以获得更高的限流上限：
    // apiKey: 'fc-YOUR-API-KEY',
  });

  // 1. Scrape Amazon's homepage
  const result = await app.scrape('https://www.amazon.com', { formats: ['markdown'] });
  const scrapeId = result.metadata?.scrapeId;

  // 2. Interact — search for a product and get its price
  await app.interact(scrapeId, { prompt: 'Search for iPhone 16 Pro Max' });
  const response = await app.interact(scrapeId, { prompt: 'Click on the first result and tell me the price' });
  console.log(response.output);

  // 3. Stop the session
  await app.stopInteraction(scrapeId);
  ```

  ```bash cURL theme={null}
  # 1. 抓取 Amazon 首页
  # 无需 API 密钥即可开始 — 添加 -H "Authorization: Bearer $FIRECRAWL_API_KEY" 以获得更高限流：
  RESPONSE=$(curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
    -H "Content-Type: application/json" \
    -d '{"url": "https://www.amazon.com", "formats": ["markdown"]}')

  SCRAPE_ID=$(echo $RESPONSE | jq -r '.data.metadata.scrapeId')

  # 2. 交互 — 搜索商品并获取价格
  curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
    -H "Content-Type: application/json" \
    -d '{"prompt": "Search for iPhone 16 Pro Max"}'

  curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
    -H "Content-Type: application/json" \
    -d '{"prompt": "Click on the first result and tell me the price"}'

  # 3. 停止会话
  curl -s -X DELETE "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact"
  ```

  ```bash CLI theme={null}
  # 1. 抓取 Amazon 首页（scrape ID 自动保存）
  firecrawl scrape https://www.amazon.com

  # 2. 交互 — 搜索商品并获取价格
  firecrawl interact "Search for iPhone 16 Pro Max"
  firecrawl interact "Click on the first result and tell me the price"

  # 3. 停止会话
  firecrawl interact stop
  ```
</CodeGroup>

```json Response theme={null}
{
  "success": true,
  "cdpUrl": "wss://browser.firecrawl.dev/...",
  "liveViewUrl": "https://liveview.firecrawl.dev/...",
  "interactiveLiveViewUrl": "https://liveview.firecrawl.dev/...",
  "output": "The iPhone 16 Pro Max (256GB) is priced at $1,199.00.",
  "exitCode": 0,
  "killed": false
}
```

<div id="interact-via-prompting">
  ## 通过 prompt 交互
</div>

这是与页面交互的最简单方式。用自然语言描述你的需求，它会自动点击、输入、滚动并提取数据。

<CodeGroup>
  ```python Python theme={null}
  response = app.interact(scrape_id, prompt="What are the customer reviews saying about battery life?")
  print(response.output)
  ```

  ```js Node theme={null}
  const response = await app.interact(scrapeId, {
    prompt: 'What are the customer reviews saying about battery life?',
  });
  console.log(response.output);
  ```

  ```bash cURL theme={null}
  # 无需 API 密钥即可开始使用 — 添加 -H "Authorization: Bearer $FIRECRAWL_API_KEY" 以获得更高的限流配额：
  curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
    -H "Content-Type: application/json" \
    -d '{
      "prompt": "What are the customer reviews saying about battery life?"
    }'
  ```

  ```bash CLI theme={null}
  firecrawl interact "What are the customer reviews saying about battery life?"
  ```
</CodeGroup>

响应中包含一个 `output` 字段，其中包含代理的答案：

```json Response theme={null}
{
  "success": true,
  "cdpUrl": "wss://browser.firecrawl.dev/...",
  "liveViewUrl": "https://liveview.firecrawl.dev/...",
  "interactiveLiveViewUrl": "https://liveview.firecrawl.dev/...",
  "output": "Customers are generally positive about battery life. Most reviewers report 8-10 hours of use on a single charge. A few noted it drains faster with heavy multitasking.",
  "stdout": "...",
  "result": "...",
  "stderr": "",
  "exitCode": 0,
  "killed": false
}
```

<div id="keep-prompts-small-and-focused">
  ### 保持 prompt 简短且聚焦
</div>

当每个 prompt 都是**单一且明确的任务**时，效果最好。不要一次性要求代理完成复杂的多步骤工作流，而应将其拆分为单独的交互调用。每次调用都会复用同一个浏览器会话，因此状态会在调用之间延续。

<div id="running-code">
  ## 运行代码
</div>

若要实现完全控制，你可以直接在浏览器沙箱中执行代码。`page` 变量 (一个 Playwright Page 对象) 可在 Node.js 和 Python 中使用。Bash 模式已预装 [agent-browser](https://github.com/vercel-labs/agent-browser)。你还可以在当前会话中截取屏幕截图：在 Node.js 中使用 `(await page.screenshot()).toString("base64")`，在 Python 中使用 `await page.screenshot(path="/tmp/screenshot.png")`，或在 Bash 中使用 `agent-browser screenshot`。

<div id="nodejs-playwright">
  ### Node.js (Playwright)
</div>

默认语言。可直接编写 Playwright 代码。`page` 已连接到浏览器。

<CodeGroup>
  ```python Python theme={null}
  response = app.interact(scrape_id, code="""
  // 点击按钮并等待页面导航
  await page.click('#next-page');
  await page.waitForLoadState('networkidle');

  // 从新页面提取内容
  const title = await page.title();
  const content = await page.$eval('.article-body', el => el.textContent);
  JSON.stringify({ title, content });
  """)
  print(response.result)
  ```

  ```js Node theme={null}
  const response = await app.interact(scrapeId, {
    code: `
      // 点击按钮并等待页面导航
      await page.click('#next-page');
      await page.waitForLoadState('networkidle');

      // 从新页面提取内容
      const title = await page.title();
      const content = await page.$eval('.article-body', el => el.textContent);
      JSON.stringify({ title, content });
    `,
  });
  console.log(response.result);
  ```

  ```bash cURL theme={null}
  # 无需 API 密钥即可开始使用 — 添加 -H "Authorization: Bearer $FIRECRAWL_API_KEY" 以获得更高的限流配额：
  curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
    -H "Content-Type: application/json" \
    -d '{
      "code": "await page.click(\"#next-page\"); await page.waitForLoadState(\"networkidle\"); const title = await page.title(); JSON.stringify({ title });",
      "language": "node",
      "timeout": 30
    }'
  ```

  ```bash CLI theme={null}
  # 自动使用最近一次的 scrape
  firecrawl interact -c "
    await page.click('#next-page');
    await page.waitForLoadState('networkidle');
    const title = await page.title();
    const content = await page.\$eval('.article-body', el => el.textContent);
    JSON.stringify({ title, content });
  "

  # 或显式传入 scrape ID
  # firecrawl interact <scrape-id> -c "await page.title()"
  ```
</CodeGroup>

<div id="python">
  ### Python
</div>

将 `language` 设置为 `"python"`，以使用 Playwright 的 Python API。

<CodeGroup>
  ```python Python theme={null}
  response = app.interact(
      scrape_id,
      code="""
  import json

  await page.click('#load-more')
  await page.wait_for_load_state('networkidle')

  items = await page.query_selector_all('.item')
  data = []
  for item in items:
      text = await item.text_content()
      data.append(text.strip())

  print(json.dumps(data))
  """,
      language="python",
  )
  print(response.stdout)
  ```

  ```js Node theme={null}
  const response = await app.interact(scrapeId, {
    code: `
  import json

  await page.click('#load-more')
  await page.wait_for_load_state('networkidle')

  items = await page.query_selector_all('.item')
  data = []
  for item in items:
      text = await item.text_content()
      data.append(text.strip())

  print(json.dumps(data))
  `,
    language: 'python',
  });
  console.log(response.stdout);
  ```

  ```bash cURL theme={null}
  # 无需 API 密钥即可开始使用 — 添加 -H "Authorization: Bearer $FIRECRAWL_API_KEY" 以获得更高的限流配额：
  curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
    -H "Content-Type: application/json" \
    -d '{
      "code": "import json\nawait page.click(\"#load-more\")\nawait page.wait_for_load_state(\"networkidle\")\nitems = await page.query_selector_all(\".item\")\ndata = [await i.text_content() for i in items]\nprint(json.dumps(data))",
      "language": "python"
    }'
  ```

  ```bash CLI theme={null}
  firecrawl interact --python -c "
  import json
  await page.click('#load-more')
  await page.wait_for_load_state('networkidle')
  items = await page.query_selector_all('.item')
  data = [await i.text_content() for i in items]
  print(json.dumps(data))
  "
  ```
</CodeGroup>

<div id="bash-agent-browser">
  ### Bash (agent-browser)
</div>

[agent-browser](https://github.com/vercel-labs/agent-browser) 是一个预装在沙箱中的 CLI 工具，提供 60 多个命令。它会提供带有元素引用 (`@e1`、`@e2` 等) 的辅助功能树，非常适合由 LLM 驱动的自动化。

<CodeGroup>
  ```python Python theme={null}
  # 拍摄快照以查看交互元素
  snapshot = app.interact(
      scrape_id,
      code="agent-browser snapshot -i",
      language="bash",
  )
  print(snapshot.stdout)
  # 输出：
  # [document]
  #   @e1 [input type="text"] "Search..."
  #   @e2 [button] "Search"
  #   @e3 [link] "About"

  # 使用 @refs 与元素交互
  app.interact(
      scrape_id,
      code='agent-browser fill @e1 "firecrawl" && agent-browser click @e2',
      language="bash",
  )
  ```

  ```js Node theme={null}
  // 拍摄快照以查看可交互元素
  const snapshot = await app.interact(scrapeId, {
    code: 'agent-browser snapshot -i',
    language: 'bash',
  });
  console.log(snapshot.stdout);
  // 输出：
  // [document]
  //   @e1 [input type="text"] "Search..."
  //   @e2 [button] "Search"
  //   @e3 [link] "About"

  // 使用 @refs 与元素进行交互
  await app.interact(scrapeId, {
    code: 'agent-browser fill @e1 "firecrawl" && agent-browser click @e2',
    language: 'bash',
  });
  ```

  ```bash cURL theme={null}
  # 拍摄快照以查看交互元素
  # 无需 API 密钥即可开始使用 — 添加 -H "Authorization: Bearer $FIRECRAWL_API_KEY" 以获得更高限流：
  curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
    -H "Content-Type: application/json" \
    -d '{"code": "agent-browser snapshot -i", "language": "bash"}'

  # Interact with elements using @refs
  curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
    -H "Content-Type: application/json" \
    -d '{"code": "agent-browser fill @e1 \"firecrawl\" && agent-browser click @e2", "language": "bash"}'
  ```

  ```bash CLI theme={null}
  # 拍摄快照以查看交互元素
  firecrawl interact --bash -c "agent-browser snapshot -i"

  # 使用 @refs 与元素交互
  firecrawl interact --bash -c 'agent-browser fill @e1 "firecrawl" && agent-browser click @e2'
  ```
</CodeGroup>

常见的 agent-browser 命令：

| 命令                        | 描述                |
| ------------------------- | ----------------- |
| `snapshot`                | 带元素引用的完整辅助功能树     |
| `snapshot -i`             | 仅显示可交互元素          |
| `click @e1`               | 通过引用点击元素          |
| `fill @e1 "text"`         | 清空字段并输入文本         |
| `type @e1 "text"`         | 不清空直接输入           |
| `press Enter`             | 按下键盘按键            |
| `scroll down 500`         | 向下滚动 500 像素       |
| `get text @e1`            | 获取文本内容            |
| `get url`                 | 获取当前 URL          |
| `wait @e1`                | 等待元素出现            |
| `wait --load networkidle` | 等待网络空闲            |
| `find text "X" click`     | 按文本查找元素并点击        |
| `screenshot`              | 对当前页面进行截图         |
| `eval "js code"`          | 在页面中运行 JavaScript |

<div id="live-view">
  ## 实时视图
</div>

每个交互响应都会返回一个 `liveViewUrl`，你可以将其嵌入页面中，以实时查看浏览器画面。适用于调试、演示或构建基于浏览器的 UI。

```json Response theme={null}
{
  "success": true,
  "cdpUrl": "wss://browser.firecrawl.dev/...",
  "liveViewUrl": "https://liveview.firecrawl.dev/...",
  "interactiveLiveViewUrl": "https://liveview.firecrawl.dev/...",
  "stdout": "",
  "result": "...",
  "exitCode": 0
}
```

```html theme={null}
<iframe src="LIVE_VIEW_URL" width="100%" height="600" />
```

<div id="interactive-live-view">
  ### 交互式实时视图
</div>

响应还包含一个 `interactiveLiveViewUrl`。与仅可查看的标准实时视图不同，交互式实时视图允许用户通过嵌入式流直接点击、输入，并与浏览器会话交互。这对于构建面向用户的浏览器 UI 很有帮助，例如登录流程，或需要终端用户控制浏览器的引导式工作流。

```html theme={null}
<iframe src="INTERACTIVE_LIVE_VIEW_URL" width="100%" height="600" />
```

<div id="cdp-url">
  ### CDP URL
</div>

每个交互响应也会返回一个 `cdpUrl`：即该浏览器会话的原始 Chrome DevTools Protocol (CDP) WebSocket URL。你可以用它从 Playwright、Puppeteer 或任何 CDP 客户端直接连接到实时会话，并通过自己的代码控制浏览器。

```js theme={null}
import { chromium } from "playwright";

const browser = await chromium.connectOverCDP(cdpUrl);
const context = browser.contexts()[0];
const page = context.pages()[0];
```

<div id="session-lifecycle">
  ## 会话生命周期
</div>

<div id="creation">
  ### 创建
</div>

首次调用 `POST /v2/scrape/{scrapeId}/interact` 会延续抓取会话并启动交互。

<div id="reuse">
  ### 复用
</div>

对同一个 `scrapeId` 的后续 interact 调用会复用现有会话。浏览器会保持打开状态，并在调用之间保留其状态，因此你可以将多个交互串联起来：

<CodeGroup>
  ```python Python theme={null}
  # 第一次调用：点击一个标签页
  app.interact(scrape_id, code="await page.click('#tab-2')")

  # 第二次调用：该标签页仍保持选中状态，提取其内容
  result = app.interact(scrape_id, code="await page.$eval('#tab-2-content', el => el.textContent)")
  print(result.result)
  ```

  ```js Node theme={null}
  // 第一次调用：点击一个标签页
  await app.interact(scrapeId, { code: "await page.click('#tab-2')" });

  // 第二次调用：该标签页仍保持选中状态，提取其内容
  const result = await app.interact(scrapeId, {
    code: "await page.$eval('#tab-2-content', el => el.textContent)",
  });
  console.log(result.result);
  ```

  ```bash CLI theme={null}
  # 第一次调用：点击一个标签页
  firecrawl interact -c "await page.click('#tab-2')"

  # 第二次调用：该标签页仍保持选中状态，提取其内容
  firecrawl interact -c "await page.\$eval('#tab-2-content', el => el.textContent)"
  ```
</CodeGroup>

<div id="cleanup">
  ### 清理
</div>

完成后请显式停止会话：

<CodeGroup>
  ```python Python theme={null}
  app.stop_interaction(scrape_id)
  ```

  ```js Node theme={null}
  await app.stopInteraction(scrapeId);
  ```

  ```bash cURL theme={null}
  # 无需 API 密钥即可开始使用 — 添加 -H "Authorization: Bearer $FIRECRAWL_API_KEY" 以获得更高的限流配额：
  curl -s -X DELETE "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact"
  ```

  ```bash CLI theme={null}
  # 停止最后一个 scrape 会话
  firecrawl interact stop

  # 或通过 ID 停止特定会话
  # firecrawl interact stop <scrape-id>
  ```
</CodeGroup>

会话也会根据 TTL (默认值：10 分钟) 或无活动 timeout (默认值：5 分钟) 自动过期。

<Warning>
  请务必在使用完毕后停止会话，以避免不必要的计费。额度按秒折算。
</Warning>

<div id="persistent-profiles-with-scrape-interact">
  ## 使用 抓取 + 交互 的持久化配置文件
</div>

默认情况下，每个 scrape + 交互 会话都会从全新的浏览器状态开始。使用 `profile`，你可以在多次抓取之间保存并复用浏览器状态 (cookies、localStorage、会话) 。这对于保持登录状态和保留偏好设置非常有用。

在初始 `POST /v2/scrape` 请求中传入 `profile` 对象。不要在 `POST /v2/scrape/{scrapeId}/interact` 中传入 `profile`；交互 会话会复用抓取任务的浏览器会话和 `profile` 设置。使用 `DELETE /v2/scrape/{scrapeId}/interact` 停止 交互 会话，以便保存对可写配置文件所做的更改。

```bash cURL theme={null}
curl -X POST "https://api.firecrawl.dev/v2/scrape" \
  -H "Authorization: Bearer fc-YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "formats": ["markdown"],
    "profile": {
      "name": "my-profile",
      "saveChanges": true
    }
  }'

curl -X POST "https://api.firecrawl.dev/v2/scrape/SCRAPE_ID/interact" \
  -H "Authorization: Bearer fc-YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Click the login button"
  }'

curl -X DELETE "https://api.firecrawl.dev/v2/scrape/SCRAPE_ID/interact" \
  -H "Authorization: Bearer fc-YOUR_API_KEY"
```

配置文件的生命周期如下：

1. 使用 `profile.name` 和 `saveChanges: true` 创建抓取。
2. 针对返回的 `scrapeId` 运行 prompt 或代码交互。
3. 停止会话以保存 cookies、localStorage 和其他浏览器状态。
4. 稍后使用相同的 `profile.name` 启动新的抓取。当你只想读取现有状态而不将更改写回时，使用 `saveChanges: false`。

<CodeGroup>
  ```python Python theme={null}
  from firecrawl import Firecrawl

  app = Firecrawl(
    # 无需 API 密钥即可开始使用——添加一个以获得更高的限流配额：
    # api_key="fc-YOUR-API-KEY",
  )

  # Session 1: Scrape with a profile, log in, then stop (state is saved)
  result = app.scrape(
      "https://app.example.com/login",
      formats=["markdown"],
      profile={"name": "my-app", "save_changes": True},
  )
  scrape_id = result.metadata.scrape_id

  app.interact(scrape_id, prompt="Fill in user@example.com and password, then click Login")
  app.stop_interaction(scrape_id)

  # Session 2: Scrape with the same profile in read-only mode - already logged in
  result = app.scrape(
      "https://app.example.com/dashboard",
      formats=["markdown"],
      profile={"name": "my-app", "save_changes": False},
  )
  scrape_id = result.metadata.scrape_id

  response = app.interact(scrape_id, prompt="Extract the dashboard data")
  print(response.output)
  app.stop_interaction(scrape_id)
  ```

  ```js Node theme={null}
  import { Firecrawl } from 'firecrawl';

  const app = new Firecrawl({
    // 无需 API 密钥即可开始使用——添加一个以获得更高的限流配额：
    // apiKey: 'fc-YOUR-API-KEY',
  });

  // Session 1: Scrape with a profile, log in, then stop (state is saved)
  const result1 = await app.scrape('https://app.example.com/login', {
    formats: ['markdown'],
    profile: { name: 'my-app', saveChanges: true },
  });
  const scrapeId1 = result1.metadata?.scrapeId;

  await app.interact(scrapeId1, { prompt: 'Fill in user@example.com and password, then click Login' });
  await app.stopInteraction(scrapeId1);

  // 会话 2：使用相同的配置文件以只读模式进行抓取——已登录
  const result2 = await app.scrape('https://app.example.com/dashboard', {
    formats: ['markdown'],
    profile: { name: 'my-app', saveChanges: false },
  });
  const scrapeId2 = result2.metadata?.scrapeId;

  const response = await app.interact(scrapeId2, { prompt: 'Extract the dashboard data' });
  console.log(response.output);
  await app.stopInteraction(scrapeId2);
  ```

  ```bash cURL theme={null}
  # Session 1: Scrape with a profile
  # 无需 API 密钥即可开始 — 添加 -H "Authorization: Bearer $FIRECRAWL_API_KEY" 以获得更高限流：
  RESPONSE=$(curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://app.example.com/login",
      "formats": ["markdown"],
      "profile": { "name": "my-app", "saveChanges": true }
    }')

  SCRAPE_ID=$(echo $RESPONSE | jq -r '.data.metadata.scrapeId')

  # Log in via interact
  curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
    -H "Content-Type: application/json" \
    -d '{"prompt": "Fill in user@example.com and password, then click Login"}'

  # 停止 — 状态已保存到配置文件
  curl -s -X DELETE "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact"

  # 会话 2：以只读模式使用相同配置文件再次抓取 — 已登录
  RESPONSE=$(curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://app.example.com/dashboard",
      "formats": ["markdown"],
      "profile": { "name": "my-app", "saveChanges": false }
    }')

  SCRAPE_ID=$(echo $RESPONSE | jq -r '.data.metadata.scrapeId')

  curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
    -H "Content-Type: application/json" \
    -d '{"prompt": "Extract the dashboard data"}'
  ```

  ```bash CLI theme={null}
  # 会话 1：使用配置文件 scrape，登录，然后停止（状态已保存）
  firecrawl scrape https://app.example.com/login --profile my-app
  firecrawl interact "Fill in user@example.com and password, then click Login"
  firecrawl interact stop

  # 会话 2：使用相同配置文件 scrape —— 已登录
  firecrawl scrape https://app.example.com/dashboard --profile my-app
  firecrawl interact "Extract the dashboard data"
  firecrawl interact stop

  # 只读：加载配置文件状态，不将 changes 写回保存
  firecrawl scrape https://app.example.com/dashboard --profile my-app --no-save-changes
  ```
</CodeGroup>

| 参数            | 默认值    | 描述                                                                               |
| ------------- | ------ | -------------------------------------------------------------------------------- |
| `name`        | None   | 持久化配置文件的名称。名称相同的抓取会共享浏览器状态。                                                      |
| `saveChanges` | `true` | 当为 `true` 时，交互 会话停止后会将浏览器状态保存回该配置文件。设为 `false` 可在不写入的情况下加载现有数据，这在你需要多个并发读取方时很有用。 |

<Note>
  同一时间只能有一个会话保存到某个配置文件。如果另一个会话已在保存，你将收到 `409` 错误。你仍然可以使用 `saveChanges: false` 打开同一个配置文件，或稍后重试。
</Note>

浏览器状态会在 交互 会话停止时保存。完成后请务必停止该会话，以便该配置文件可以被复用。

<div id="validate-persistence">
  ### 验证持久化
</div>

你可以在一个会话中写入 `localStorage` 值并停止该会话，然后在第二个使用相同配置文件的会话中读取该值，以此测试持久化，而无需依赖真实的登录流程。

```bash cURL theme={null}
# 会话 1：写入浏览器状态并保存
RESPONSE=$(curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
  -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "formats": ["markdown"],
    "profile": { "name": "profile-validation", "saveChanges": true }
  }')

SCRAPE_ID=$(echo "$RESPONSE" | jq -r ".data.metadata.scrapeId")

curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
  -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "await page.evaluate(() => { localStorage.setItem(\"firecrawlProfileCheck\", \"saved\"); document.cookie = \"firecrawl_profile_check=saved; path=/; max-age=3600\"; return localStorage.getItem(\"firecrawlProfileCheck\"); });"
  }'

curl -s -X DELETE "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
  -H "Authorization: Bearer $FIRECRAWL_API_KEY"

# 会话 2：以只读模式加载相同配置文件并验证值
RESPONSE=$(curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
  -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "formats": ["markdown"],
    "profile": { "name": "profile-validation", "saveChanges": false }
  }')

SCRAPE_ID=$(echo "$RESPONSE" | jq -r ".data.metadata.scrapeId")

curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
  -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "await page.evaluate(() => ({ localStorage: localStorage.getItem(\"firecrawlProfileCheck\"), cookie: document.cookie.includes(\"firecrawl_profile_check=saved\") }));"
  }'

curl -s -X DELETE "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
  -H "Authorization: Bearer $FIRECRAWL_API_KEY"
```

第二个交互响应应显示 `localStorage` 为 `"saved"`，`cookie` 为 `true`。

<Info>
  通过 API 创建的 Profiles 可能暂时还不会显示在 Dashboard > Interact > Profiles 中。Dashboard 目前尚未提供通过 API 创建的持久化 Profiles 的完整列表。
</Info>

<div id="when-to-use-what">
  ## 何时使用什么
</div>

| 使用场景         | 推荐                            | 原因                  |
| ------------ | ----------------------------- | ------------------- |
| 网页搜索         | [Search](/zh/features/search) | 专用搜索端点              |
| 从 URL 获取干净内容 | [Scrape](/zh/features/scrape) | 一次 API 调用，无需会话      |
| 在页面上点击、输入、导航 | **交互** (prompt)               | 只需用英文描述即可           |
| 提取交互后的数据     | **交互** (prompt)               | 无需选择器               |
| 复杂的抓取逻辑      | **交互** (code)                 | 完整的 Playwright 控制能力 |

<Info>
  **交互 与 浏览器沙箱**：交互构建在与 [浏览器沙箱](/zh/features/browser) 相同的基础设施之上，但针对最常见的使用模式提供了更好的界面：先抓取页面，再进一步深入。当你需要一个不绑定到特定抓取任务的独立浏览器会话时，浏览器沙箱更合适。
</Info>

<div id="pricing">
  ## 定价
</div>

* **仅代码** (无 `prompt`): 每个会话分钟 2 个额度
* **使用 AI prompts**: 每个会话分钟 7 个额度
* **抓取**: 单独计费 (每次抓取 1 个额度，外加任何特定格式的费用) 。

<div id="api-reference">
  ## API 参考
</div>

* [执行交互](/zh/api-reference/endpoint/scrape-execute): `POST /v2/scrape/{scrapeId}/interact`
* [停止交互](/zh/api-reference/endpoint/scrape-browser-delete): `DELETE /v2/scrape/{scrapeId}/interact`

<div id="request-body-post">
  ### 请求体 (POST)
</div>

| 字段         | 类型       | 默认值      | 描述                                                                  |
| ---------- | -------- | -------- | ------------------------------------------------------------------- |
| `prompt`   | `string` | None     | 提供给 AI 代理的自然语言任务。若未设置 `code`，则此项必填。最多 10,000 个字符。                   |
| `code`     | `string` | None     | 要执行的代码 (Node.js、Python 或 Bash) 。若未设置 `prompt`，则此项必填。最多 100,000 个字符。 |
| `language` | `string` | `"node"` | `"node"`、`"python"` 或 `"bash"`。仅在使用 `code` 时生效。                     |
| `timeout`  | `number` | `30`     | 超时时间，单位为秒 (1–300) 。                                                 |
| `origin`   | `string` | None     | 用于活动追踪的调用方标识符。                                                      |

<div id="response">
  ### 响应
</div>

| 字段                       | 描述                                                                                               |
| ------------------------ | ------------------------------------------------------------------------------------------------ |
| `success`                | 如果执行已完成且未出现错误，则为 `true`                                                                          |
| `cdpUrl`                 | 浏览器会话的原始 Chrome DevTools Protocol (CDP) WebSocket URL。可直接使用 Playwright、Puppeteer 或任何 CDP 客户端进行连接 |
| `liveViewUrl`            | 浏览器会话的只读实时视图 URL                                                                                 |
| `interactiveLiveViewUrl` | 交互式实时视图 URL (查看者可控制浏览器)                                                                          |
| `output`                 | 代理对你的 `prompt` 给出的自然语言回答。仅在使用 `prompt` 时返回。                                                      |
| `stdout`                 | 代码执行的标准输出                                                                                        |
| `result`                 | 沙箱的原始返回值。对于 `code`：最后一个求值的表达式。对于 `prompt`：代理用于生成 `output` 的原始页面快照。                               |
| `stderr`                 | 标准错误输出                                                                                           |
| `exitCode`               | 退出码 (`0` = 成功)                                                                                   |
| `killed`                 | 如果执行因超时而终止，则为 `true`                                                                             |

***

有反馈或需要帮助？请发送邮件至 [help@firecrawl.com](mailto:help@firecrawl.com)，或通过 [Discord](https://discord.gg/firecrawl) 联系我们。
