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

# 结果导出到 Amazon S3、Google Cloud Storage 和 BigQuery

> 将每个已结束的任务和每条已评分的监测器回答，以每小时或每天的 JSON Lines 文件发送到你自己的存储桶，或写入 BigQuery 表分区。

导出每小时或每个 UTC 日一次，把该窗口的结果以 gzip 压缩的 JSON Lines 写入 Amazon S3 或 Google Cloud
Storage 存储桶，或加载到 BigQuery 表的一个分区。你的数据仓库像读取其他数据源一样读取这些结果，无需轮询。

你只需为我们的导出身份授予存储桶或数据集的写入权限，无需共享任何密钥或密码。本页中的密钥、存储桶、
账户 ID 和角色均为示例。

## 导出的数据

| 数据集 | 行 | 时间窗口依据 | 字段与之相同 |
| - | - | - | - |
| `tasks` | 账户中以 `COMPLETED` 或 `FAILED` 结束的所有任务 | `completedAt` | [`GET /v1/async/task/{id}`](/zh/api-reference/get-task) 和 webhook |
| `monitor_results` | 账户中所有监测器的所有已评分回答 | `ranAt` | [`GET /v1/monitors/{id}/results?includeEvidence=true`](/zh/api-reference/monitors/export-results) |

使用账户任意密钥提交的任务，以及 Playground 和监测器创建的任务都会包含在内。一个窗口恰好包含时间落在
`[windowStart, windowEnd)` 内的行。

**送达时间。** 窗口关闭 10 分钟后到期，因此在边界结算的费用也会包含在内。到期的窗口由每 15 分钟运行一次的
作业取走，因此通常在窗口关闭后 10 到 25 分钟开始发送。

## 设置导出

**1. 查看导出身份**

```bash theme={"theme":"css-variables"}
curl https://api.querying.ai/v1/exports/identity \
  -H "Authorization: Bearer $QUERYING_API_KEY"
```

```json theme={"theme":"css-variables"}
{
  "success": true,
  "identity": {
    "accountKey": "acct_3f9a1c2b7d4e",
    "gcp": { "serviceAccount": "querying-export@example-project.iam.gserviceaccount.com" },
    "aws": {
      "federatedPrincipal": "accounts.google.com",
      "audience": "117000000000000000001",
      "oaud": "querying-result-export",
      "roleSessionName": "querying-acct_3f9a1c2b7d4e",
      "trustPolicy": { "Version": "2012-10-17", "Statement": ["..."] }
    }
  }
}
```

`accountKey` 是你的文件夹和表的名称。我们写入的每个路径和表都带有它，因此多个 querying.ai 账户
可以共用一个存储桶或数据集，各自只写入自己的文件。请授权响应中显示的身份，而不是上面的示例。

**2. 授予权限**

为该身份授予目标位置的访问权限。S3、Cloud Storage 和 BigQuery 的做法见[授予权限](#授予权限)。

**3. 创建导出**

`POST /v1/exports` 会检查授权并保存导出。见[创建导出](#创建导出)。

**4. 查看首次发送**

`GET /v1/exports/{id}/runs` 列出每个窗口及其状态。见[发送记录](#发送记录)。

## 授予权限

### Amazon S3

在你的 AWS 账户中创建一个可由我们的 Google 身份代入的 IAM 角色，并将身份响应中的 `trustPolicy` 用作该
角色的信任策略。它只接受我们的导出身份，并且只接受你账户的会话名称：

```json theme={"theme":"css-variables"}
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "Federated": "accounts.google.com" },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": { "StringEquals": {
      "accounts.google.com:aud": "117000000000000000001",
      "accounts.google.com:oaud": "querying-result-export",
      "sts:RoleSessionName": "querying-acct_3f9a1c2b7d4e"
    } }
  }]
}
```

附加一个权限策略，允许该角色写入你的前缀：

```json theme={"theme":"css-variables"}
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "s3:PutObject",
    "Resource": "arn:aws:s3:::acme-geo/querying/*"
  }]
}
```

如果存储桶使用客户管理的 KMS 密钥加密，还需在该密钥上允许 `kms:GenerateDataKey`。

### Google Cloud Storage

在存储桶上为导出服务账户授予 **Storage Object User**（`roles/storage.objectUser`）。它用于创建文件，
并在重新发送窗口时替换文件。

```bash theme={"theme":"css-variables"}
gcloud storage buckets add-iam-policy-binding gs://acme-geo \
  --member="serviceAccount:querying-export@example-project.iam.gserviceaccount.com" \
  --role="roles/storage.objectUser"
```

如需把授权限定在你的文件夹，请添加 IAM 条件
`resource.name.startsWith("projects/_/buckets/acme-geo/objects/querying/acct_3f9a1c2b7d4e/")`。

### BigQuery

在数据集上为导出服务账户授予 **BigQuery Data Editor**（`roles/bigquery.dataEditor`）。加载作业在我们自己的
项目中运行，因此加载不产生费用；你只需支付存储和你运行的查询费用。

```bash theme={"theme":"css-variables"}
bq add-iam-policy-binding \
  --member="serviceAccount:querying-export@example-project.iam.gserviceaccount.com" \
  --role="roles/bigquery.dataEditor" acme-analytics:geo_raw
```

## 创建导出

请使用没有引擎范围限制的 API 密钥：导出包含账户的全部结果，因此仅限部分引擎的密钥会收到 `403 KEY_SCOPE`。

```bash Amazon S3 theme={"theme":"css-variables"}
curl -X POST https://api.querying.ai/v1/exports \
  -H "Authorization: Bearer $QUERYING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "destination": {
      "type": "s3",
      "bucket": "acme-geo",
      "region": "us-east-1",
      "roleArn": "arn:aws:iam::123456789012:role/querying-export",
      "prefix": "querying"
    },
    "interval": "daily",
    "data": ["tasks", "monitor_results"]
  }'
```

```bash Google Cloud Storage theme={"theme":"css-variables"}
curl -X POST https://api.querying.ai/v1/exports \
  -H "Authorization: Bearer $QUERYING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "destination": { "type": "gcs", "bucket": "acme-geo", "prefix": "querying" },
    "interval": "hourly",
    "data": ["tasks"]
  }'
```

```bash BigQuery theme={"theme":"css-variables"}
curl -X POST https://api.querying.ai/v1/exports \
  -H "Authorization: Bearer $QUERYING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "destination": { "type": "bigquery", "projectId": "acme-analytics", "datasetId": "geo_raw" },
    "interval": "daily",
    "startAt": "2026-10-01T00:00:00Z"
  }'
```

| 字段 | 必填 | 说明 |
| - | - | - |
| `destination.type` | 是 | `s3`、`gcs` 或 `bigquery`。 |
| `destination.bucket` | S3、GCS | 存储桶名称。 |
| `destination.prefix` | 否 | 存储桶内的文件夹，最多 256 个字符，须为相对路径（不以 `/` 开头，不含 `.` 或 `..` 段）。末尾会自动加上 `/`。 |
| `destination.region` | S3 | 存储桶所在的 AWS 区域，例如 `us-east-1`。 |
| `destination.roleArn` | S3 | [授予权限](#amazon-s3)中创建的角色，格式为 `arn:aws:iam::<account>:role/<name>`。 |
| `destination.projectId`、`destination.datasetId` | BigQuery | Google Cloud 项目和数据集。 |
| `interval` | 否 | `hourly` 或 `daily`（默认）。在导出的整个生命周期内保持不变。 |
| `data` | 否 | `tasks`、`monitor_results` 或两者（默认）。 |
| `startAt` | 否 | ISO 8601 时间，不能是未来时间，最多回溯 30 天。第一个窗口是包含该时间的窗口。默认为现在。 |

保存前，导出会先验证授权。S3 和 Cloud Storage 会写入
`<prefix><accountKey>/_querying/connection-check.json`；BigQuery 会读取数据集并记录其位置。检查失败时
不保存任何内容。每个账户最多 10 个导出。

响应为 `201`，包含该导出：

```json theme={"theme":"css-variables"}
{
  "success": true,
  "export": {
    "id": "6a1d0f3c-...",
    "destination": {
      "type": "bigquery",
      "projectId": "acme-analytics",
      "datasetId": "geo_raw",
      "location": "US",
      "tables": [
        "acme-analytics.geo_raw.querying_tasks_v1_acct_3f9a1c2b7d4e",
        "acme-analytics.geo_raw.querying_monitor_results_v1_acct_3f9a1c2b7d4e"
      ]
    },
    "interval": "daily",
    "data": ["tasks", "monitor_results"],
    "enabled": true,
    "schemaVersion": 1,
    "nextWindowStart": "2026-10-01T00:00:00.000Z",
    "createdAt": "2026-10-19T09:00:00.000Z",
    "deliveredThrough": null,
    "failedRuns": 0,
    "retryingRuns": 0
  }
}
```

对于 S3 和 Cloud Storage，`destination` 会回显存储桶设置，并加上 `path`，即文件写入的文件夹，例如
`s3://acme-geo/querying/acct_3f9a1c2b7d4e/` 或 `gs://acme-geo/querying/acct_3f9a1c2b7d4e/`。
`nextWindowStart` 是下一个待规划窗口的开始时间。

### 错误

| 状态 | 错误码 | 原因 |
| - | - | - |
| `400` | `VALIDATION_ERROR` | JSON 无效或字段超出范围。消息会指出字段，例如 `destination.region: region is the bucket's AWS region, e.g. us-east-1`。 |
| `403` | `KEY_SCOPE` | API 密钥仅限部分引擎。 |
| `409` | `EXPORT_LIMIT` | 账户已有 10 个导出。 |
| `422` | `EXPORT_DESTINATION_UNREACHABLE` | 连接检查失败。消息中包含存储服务给出的原因，通常是缺少授权。 |
| `502` | `EXPORT_CHECK_FAILED` | 无法连接到目标位置。请重试。 |
| `503` | `EXPORTS_NOT_CONFIGURED` | 暂不支持导出到该类型的目标位置。 |

## 文件与表

### S3 和 Cloud Storage 中的文件

```text theme={"theme":"css-variables"}
s3://acme-geo/querying/acct_3f9a1c2b7d4e/tasks/v1/dt=2026-10-08/part-00000.jsonl.gz
s3://acme-geo/querying/acct_3f9a1c2b7d4e/tasks/v1/dt=2026-10-08/_manifest.json
s3://acme-geo/querying/acct_3f9a1c2b7d4e/monitor_results/v1/dt=2026-10-08/hour=13/part-00000.jsonl.gz
```

* 目录遵循 Hive 分区（`dt=`，每小时导出另有 `hour=`），因此 Athena、BigQuery 外部表和 Spark 会把它们读成列。
* `v1` 是架构版本（导出上的 `schemaVersion`）。破坏列的变更会以 `v2` 并列发布。
* 一个窗口按时间顺序拆分为每份最多 100,000 行的 part 文件。
* `_manifest.json` 最后写入。清单出现后再加载该窗口，并从清单中读取 part 列表：

```json theme={"theme":"css-variables"}
{
  "schemaVersion": 1,
  "data": "tasks",
  "windowStart": "2026-10-08T00:00:00.000Z",
  "windowEnd": "2026-10-09T00:00:00.000Z",
  "rows": 20000,
  "files": [{ "key": "querying/acct_3f9a1c2b7d4e/tasks/v1/dt=2026-10-08/part-00000.jsonl.gz", "rows": 20000, "bytes": 1990096 }],
  "deliveredAt": "2026-10-09T00:16:02.114Z"
}
```

清单中的 `rows` 等于各 part 文件的行数，也等于发送记录中该 run 的 `rows`。文件名只取决于窗口，因此重新发送
的窗口会替换相同的对象。

### BigQuery 表

BigQuery 导出加载到 `querying_tasks_v1_<accountKey>` 和 `querying_monitor_results_v1_<accountKey>`，表在首次
加载时创建，并按 `completedAt` 或 `ranAt` 分区（按天；每小时导出按小时）。每个窗口替换自己的分区，因此
重新发送的窗口不会重复。`payload`、`response`、`sources` 和 `scoringContext` 是 `JSON` 列：

```sql theme={"theme":"css-variables"}
SELECT taskType, JSON_VALUE(payload.prompt) AS prompt, JSON_VALUE(response.text) AS answer
FROM `acme-analytics.geo_raw.querying_tasks_v1_acct_3f9a1c2b7d4e`
WHERE DATE(completedAt) = "2026-10-08" AND status = "COMPLETED";
```

## 行架构 (v1)

### `tasks`

| 字段 | 类型 | BigQuery 类型 | 说明 |
| - | - | - | - |
| `id` | string | `STRING` | 任务 ID。 |
| `taskType` | string | `STRING` | 引擎，例如 `CHATGPT`。 |
| `status` | string | `STRING` | `COMPLETED` 或 `FAILED`。 |
| `priority` | integer | `INT64` | 提交时的值。 |
| `idempotencyKey` | string 或 null | `STRING` | 你发送的幂等键（如有）。 |
| `createdAt`、`completedAt` | timestamp | `TIMESTAMP` | UTC，微秒精度。 |
| `monitorId` | string 或 null | `STRING` | 运行该任务的监测器。 |
| `creditsCharged` | decimal | `NUMERIC` | 该任务扣除的积分，最多两位小数（例如 `1.25`）。失败的任务为 `0`。请按小数而非整数读取。 |
| `payload` | object | `JSON` | 你提交的 payload。 |
| `response` | object 或 null | `JSON` | 与 `GET /v1/async/task/{id}` 返回的结果相同；任务失败时为 `null`。 |
| `error` | string 或 null | `STRING` | 失败原因，以错误码开头；完成时为 `null`。 |

### `monitor_results`

| 字段 | 类型 | BigQuery 类型 | 说明 |
| - | - | - | - |
| `id`、`monitorId`、`taskId` | string | `STRING` | 结果、监测器和任务 ID。 |
| `engine`、`prompt` | string | `STRING` | 在哪里问了什么。 |
| `mentioned`、`cited` | boolean | `BOOL` | 是否提及品牌；是否引用你的域名。 |
| `position` | integer 或 null | `INT64` | 首次提及品牌的字符位置。 |
| `competitorHits` | string array | `STRING`，repeated | 回答中提到的竞品。 |
| `sources` | array | `JSON` | 引用，格式为 `{url, label, position}`。 |
| `answerText` | string 或 null | `STRING` | 评分所依据的回答。 |
| `scoringContext` | object 或 null | `JSON` | 当时的评分定义。 |
| `latitude`、`longitude` | number 或 null | `FLOAT64` | 位置监测器的提问地点。 |
| `ranAt` | timestamp | `TIMESTAMP` | UTC，微秒精度。 |

## 发送记录

每个数据集的每个窗口就是一个 run。按时间倒序列出：

```bash theme={"theme":"css-variables"}
curl "https://api.querying.ai/v1/exports/$EXPORT_ID/runs?status=failed" \
  -H "Authorization: Bearer $QUERYING_API_KEY"
```

```json theme={"theme":"css-variables"}
{
  "success": true,
  "runs": [{
    "id": "3c9e1b7a-...",
    "data": "tasks",
    "windowStart": "2026-10-08T00:00:00.000Z",
    "windowEnd": "2026-10-09T00:00:00.000Z",
    "status": "failed",
    "attempts": 8,
    "nextAttemptAt": null,
    "rows": null,
    "bytes": null,
    "files": [],
    "error": "S3 PutObject s3://acme-geo/... failed: HTTP 403 AccessDenied: Access Denied",
    "deliveredAt": null
  }],
  "nextCursor": null
}
```

| `status` | 含义 |
| - | - |
| `pending` | 等待发送，或在一次失败后等待重试。`nextAttemptAt` 表示何时进行。 |
| `running` | 正在发送。 |
| `delivered` | 已写入。`rows`、`bytes`、`files` 和 `deliveredAt` 已设置。 |
| `failed` | 八次尝试均失败。在你手动重试之前不会再有任何动作。 |

可用 `status`、`data`（`tasks` 或 `monitor_results`）和 `limit`（1–200，默认 50）筛选；把 `nextCursor` 作为
`cursor` 传回即可获取下一页。

* **自动重试。** 失败的尝试会在 15 分钟后重试，之后间隔逐次翻倍，最长 12 小时，共八次。`error` 中是存储服务
  给出的原因，因此缺少权限会首先在这里显示。
* **重新发送窗口。** `POST /v1/exports/{id}/runs/{runId}/retry` 立即发送一个窗口：修好授权后发送失败的窗口，
  或重写已送达的窗口。它会重置 `attempts`，并以 `pending` 返回该 run。正在发送的 run 会返回
  `409 EXPORT_RUN_RUNNING`。
* **导出健康状况。** `GET /v1/exports` 和 `GET /v1/exports/{id}` 会附带 `deliveredThrough`（最近送达窗口的
  结束时间）、`failedRuns` 和 `retryingRuns`（至少失败过一次、仍在等待的 run）。

## 暂停、恢复和删除

* 用 `{"enabled": false}` 调用 `PATCH /v1/exports/{id}` 可暂停导出。`{"enabled": true}` 恢复导出，并补发
  遗漏的窗口，最多回溯 30 天。
* `DELETE /v1/exports/{id}` 停止导出。已送达的文件和分区保留在你的存储中。
* 目标位置、周期和数据集无法更改。请创建新的导出并删除旧的导出。

## 端点

| 方法和路径 | 用途 |
| - | - |
| [`GET /v1/exports/identity`](/zh/api-reference/exports/get-identity) | 需要授权的身份和你的账户键 |
| [`POST /v1/exports`](/zh/api-reference/exports/create-export) | 连接目标位置 |
| [`GET /v1/exports`](/zh/api-reference/exports/list-exports) | 列出导出及其发送健康状况 |
| [`GET /v1/exports/{id}`](/zh/api-reference/exports/get-export) | 单个导出 |
| [`PATCH /v1/exports/{id}`](/zh/api-reference/exports/update-export) | 暂停或恢复 |
| [`DELETE /v1/exports/{id}`](/zh/api-reference/exports/delete-export) | 停止导出 |
| [`GET /v1/exports/{id}/runs`](/zh/api-reference/exports/list-runs) | 发送记录 |
| [`POST /v1/exports/{id}/runs/{runId}/retry`](/zh/api-reference/exports/retry-run) | 重新发送一个窗口 |

访问不属于你的导出会返回 `404 NOT_FOUND`。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.