第 2 课

拿 API key 调 Search:搜索类型、内容视图、过滤与结构化输出

创建 API key,用 curl、Python、JavaScript 各发一次搜索,学会选搜索类型、只要一种内容视图、加过滤条件和返回结构化 JSON。

图文25 分钟

课程目录第 2 / 3 课

学习位置仅保存在当前浏览器,有效期 180 天。

01 / 图文教材

图文教材

本课按 Exa 官方 Exa Search API、SDK Quickstart、Search Best Practices、Pricing 与 Error Codes 整理;SDK 版本 exa-py 2.26.0 / exa-js 2.26.0(2026-10-19 发布),报错输出为 2026-10-21 实测。 目标:创建 API key,用 curl、Python、JavaScript 各发一次搜索;学会选搜索类型、只要一种内容视图、加过滤条件,并让 Exa 直接返回结构化 JSON。

你将得到什么

  • 一个放在环境变量里的 EXA_API_KEY
  • 一条能直接复制的 curl 搜索命令,以及看懂响应的字段表
  • first_search.py 与 first_search.mjs 两个可运行脚本
  • 一张「搜索类型 × 延迟 × 价格」对照表,和一张常见报错表

一、创建 API key

  1. 打开 https://dashboard.exa.ai/api-keys 注册并创建 key。
  2. 按官方定价页:免费档注册当天给 $10 额度(约 2,500 次 Lite 搜索),每月 1 日重置为 $10,不需要绑卡;在控制台完成新手引导,首个团队再送一次性 $10。

然后把 key 放进环境变量,两个官方 SDK 都会自动读取它:

export EXA_API_KEY="your-api-key"

Windows PowerShell 用 setx EXA_API_KEY "your-api-key",然后重开终端。

key 只放环境变量或密钥管理器,不要写进代码、截图或提交到仓库。下文命令一律用 $EXA_API_KEY 引用你的 key(第三节故意写的 not-a-real-key 是假 key)。

二、第一条 curl 请求

query 是唯一必填字段,用自然语言描述你想找的页面;contents.highlights 让每条结果附带与问题相关的摘录:

curl -s -X POST "https://api.exa.ai/search" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $EXA_API_KEY" \
  -d '{
    "query": "recent techniques for improving retrieval in RAG systems",
    "type": "auto",
    "numResults": 3,
    "contents": { "highlights": true }
  }'

对照结果(官方文档示例响应,已截短;你的结果和 ID 会不同):

{
  "requestId": "c3174df2b9cd5afbc64cdf79f3719b19",
  "results": [
    {
      "title": "Four retrieval techniques to improve RAG you need to know",
      "url": "https://www.thoughtworks.com/en-us/insights/blog/generative-ai/four-retrieval-techniques-improve-rag",
      "publishedDate": "2025-04-14T00:00:00.000Z",
      "highlights": ["It's not surprising, then, that we've seen a range of ..."]
    }
  ],
  "searchTime": 1324.3,
  "costDollars": { "total": 0.007, "search": { "neural": 0.007 } }
}
字段含义
requestId本次请求 ID,找官方支持时要提供
results[].title / url / publishedDate页面元数据,按相关度排序
results[].highlights与查询相关的原文摘录,适合直接喂给模型
searchTime耗时(毫秒)
costDollars.total本次花费(美元);auto 档 $7/千次,即 $0.007 一次

默认返回 10 条,numResults 最多 100;Search 不支持翻页。

三、key 出错时长什么样

这两种情况我们都实测过,先认识它们,排错会快很多:

curl -s -X POST "https://api.exa.ai/search" -H "Content-Type: application/json" \
  -H "Authorization: Bearer not-a-real-key" -d '{"query":"test"}'

对照结果(实测,HTTP 401):

{"requestId":"...","error":"Invalid API key. Provide a valid key using
'Authorization: Bearer <key>' or 'x-api-key: <key>'. ...","tag":"INVALID_API_KEY"}

如果完全没带 key(比如环境变量没生效,$EXA_API_KEY 展开成空),实测返回 HTTP 402,tag 是 X402_PAYMENT_REQUIRED:这是 Exa 的免 key「按次用 USDC 付费」通道在要付款,不是你的余额不足。看到它就先运行 echo ${#EXA_API_KEY},确认长度不是 0。

四、选对搜索类型

type 决定速度、深度和价格,默认 auto 适合大多数场景:

type适合典型延迟价格(含 10 条)
auto默认,质量与速度平衡约 1 秒$7 / 千次
lite高频、对成本或延迟敏感约 450 毫秒$4 / 千次
deep-lite轻量调研与综合约 4 秒$12 / 千次
deep多步检索、结构化输出4–15 秒$12 / 千次

超过 10 条的结果每千条另加 $1。fast、instant、deep-reasoning 是官方标注的旧类型,新代码请分别改用 lite、lite、deep。

lite 只接受一部分参数,用了不支持的(如 contents.summary、outputSchema、maxAgeHours)会直接返回 400 INVALID_REQUEST_BODY 并点名是哪个参数,而不是悄悄忽略。

五、每次只要一种内容视图

contents 里有三种视图,官方建议一次只选一种,同时要多种会分别计费:

  • "highlights": true:推荐默认值,按相关度自动决定摘录长度
  • "text": { "maxCharacters": 10000 }:要整页结构或完整上下文时用,记得限长
  • "summary":每条结果额外调用一次模型,最贵也最慢

在 /search 里这些字段必须放在 contents 里面;第 3 课的 /contents 接口则相反,直接放在顶层。

实测提醒:用 Python SDK 时如果完全不写 contents,exa-py 2.26.0 会自动附带 "contents": {"text": {"maxCharacters": 10000}},也就是默认要整页文字。想要摘录就显式写 contents={"highlights": True};什么内容都不要,写 contents=False。

六、加过滤条件

过滤是硬约束:结果超出范围就没用时才加,软偏好写进查询语句即可。

  • includeDomains:只看这些站点,支持路径前缀(anthropic.com/news)和通配子域(*.substack.com)
  • excludeDomains:排除站点
  • startPublishedDate / endPublishedDate:按发布日期,ISO 8601 格式
  • category:company、people、news、publication、personal site、financial report;其中 company、people 不支持发布日期过滤

七、写成 Python 脚本

python3 -m venv .venv && source .venv/bin/activate
pip install exa-py

exa-py 需要 Python 3.9+。新建 first_search.py:

from exa_py import Exa

exa = Exa()  # 读取环境变量 EXA_API_KEY

result = exa.search(
    "Exa 3.0 release: what changed in search quality and pricing",
    type="auto",
    num_results=5,
    include_domains=["exa.ai"],
    start_published_date="2026-10-01T00:00:00.000Z",
    contents={"highlights": True},
)

for i, item in enumerate(result.results, 1):
    print(f"[{i}] {item.title}")
    print(f"    {item.url}  发布: {item.published_date}")
    if item.highlights:
        print(f"    要点: {item.highlights[0][:120]}")
print("本次费用(美元):", result.cost_dollars.total if result.cost_dollars else "未返回")

Python SDK 的参数一律用 snake_case,发出去时会自动转成 API 的 camelCase。我们在本机截获了它实际发送的请求体(实测):

{"query": "Exa 3.0 release: what changed in search quality and pricing",
 "contents": {"highlights": true}, "numResults": 5,
 "includeDomains": ["exa.ai"], "startPublishedDate": "2026-10-01T00:00:00.000Z",
 "type": "auto"}

运行 python first_search.py。对照结果(示例:标题、链接、发布日期取自 2026-10-21 用同样查询和过滤条件实际检索到的前 3 条;要点行为示意并已截短;费用按 auto 档官方价):

[1] Introducing Exa 3.0 | Exa Blog
    https://exa.ai/blog/exa-3  发布: 2026-10-19T00:00:00.000Z
    要点: Today we're introducing Exa 3.0, the largest improvement to search quality ...
[2] Exa 3.0 Live Q&A | Exa Live Event
    https://exa.ai/webinars/exa3.0-live  发布: 2026-10-19T20:00:00.000Z
    要点: Join Liam Hinzman, Exa's Head of R&D and Shyam Kumar, Exa's Head of Product ...
[3] What is web indexing? How it works and why AI search needs its own index | Exa Blog
    https://exa.ai/blog/what-is-web-indexing  发布: 2026-10-06T00:00:00.000Z
    要点: An LLM, by contrast, reads material supplied by a retrieval system ...
本次费用(美元): 0.007

key 无效时,SDK 会抛出 ValueError: Request failed with status code 401: ...INVALID_API_KEY(实测);没设置环境变量则在 Exa() 这一行就报 API key must be provided as an argument or in EXA_API_KEY environment variable。

八、JavaScript 版本

npm install exa-js

新建 first_search.mjs,参数名换成 camelCase:

import Exa from "exa-js";

const exa = new Exa(); // 读取环境变量 EXA_API_KEY

const result = await exa.search(
  "Exa 3.0 release: what changed in search quality and pricing",
  {
    type: "auto",
    numResults: 5,
    includeDomains: ["exa.ai"],
    startPublishedDate: "2026-10-01T00:00:00.000Z",
    contents: { highlights: true },
  },
);

result.results.forEach((item, i) => {
  console.log(`[${i + 1}] ${item.title}`);
  console.log(`    ${item.url}  发布: ${item.publishedDate}`);
  if (item.highlights?.length) console.log(`    要点: ${item.highlights[0].slice(0, 120)}`);
});
console.log("本次费用(美元):", result.costDollars?.total ?? "未返回");

运行 node first_search.mjs,输出格式与 Python 版一致。

九、让 Exa 直接给结构化答案

加上 outputSchema,Exa 会在 results 之外多返回一个 output:生成的值在 output.content,字段级来源和置信度在 output.grounding。需要综合多个来源时官方推荐配合 deep:

answer = exa.search(
    "Exa 3.0 launch announcement",
    type="deep",
    system_prompt="Prefer official exa.ai sources",
    output_schema={
        "type": "object",
        "properties": {
            "releaseDate": {"type": "string"},
            "litePricePer1k": {"type": "string"},
            "keyChanges": {"type": "array", "items": {"type": "string"}},
        },
        "required": ["releaseDate", "keyChanges"],
    },
    contents={"highlights": True},
)
print(answer.output.content if answer.output else None)

官方限制:对象 schema 最多 10 个属性(嵌套属性也算)、最多 2 层嵌套;不要在 schema 里自己加引用或置信度字段,output.grounding 会自动给出。这一步按 deep 档计费($12 / 千次)。

常见错误对照

现象原因处理
401 INVALID_API_KEYkey 写错或已删除到控制台重新复制 key
402 X402_PAYMENT_REQUIRED请求根本没带 key检查 echo ${#EXA_API_KEY} 是否为 0
402 NO_MORE_CREDITS额度用完等每月重置或充值
400 INVALID_REQUEST_BODY参数放错位置或 lite 不支持按报错点名的字段修改
429 RATE_LIMIT_EXCEEDED请求过快按 Retry-After 等待或指数退避
照抄旧教程的 useAutoprompt、livecrawl已移除或已弃用删掉 useAutoprompt;livecrawl: "always" 改成 contents.maxAgeHours: 0

下一课:用 Contents API 读取已知网址,再把 Exa 做成 Claude 可以自己调用的工具。