第 2 课
拿 API key 调 Search:搜索类型、内容视图、过滤与结构化输出
创建 API key,用 curl、Python、JavaScript 各发一次搜索,学会选搜索类型、只要一种内容视图、加过滤条件和返回结构化 JSON。
课程目录第 2 / 3 课
学习位置仅保存在当前浏览器,有效期 180 天。
图文教材
本课按 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
- 打开 https://dashboard.exa.ai/api-keys 注册并创建 key。
- 按官方定价页:免费档注册当天给 $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_KEY | key 写错或已删除 | 到控制台重新复制 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 可以自己调用的工具。
