# Reporting API 最佳实践

> 了解 Tapjoy Offerwall Reporting API 终端、请求结构和错误处理机制，从而有效获取准确的变现数据。

Reporting API 使广告主和发行商能够使用 GraphQL 查询语言从 Tapjoy 平台访问报告数据并管理 Offerwall 内容。如需有关 GraphQL 的更多信息，请参阅 GraphQL 官方[网站](https://graphql.org/)。

您可以使用 API [资源管理器](https://api.tapjoy.com/graphql/docs/explorer)来协助构建查询。选择左上角的书本图标可展开文档资源管理器，然后查找可在查询中使用的字段和指标的相关信息。

![单击书本图标后在 Reporting API 资源管理器中显示的文档资源管理器面板。](/api/media?file=/grow/media/images/reporting_api_explorer.png)

## API 终端##api-endpoint

Reporting API 只有一个终端：

```json
https://api.tapjoy.com/graphql
```

无论执行任何操作，终端都保持不变。

## 与 API 通信##communicating-with-the-api

获得访问令牌后，即可向 API 发出请求。要向 API 发起请求，无论执行查询还是变更操作，都需要通过 HTTP POST 提供 JSON 编码的请求正文。

请求示例：

以下示例展示了如何使用原始 HTTP、curl 和 Ruby 向 API 发出经过身份验证的请求。

1. **Raw**

   ```text
   POST /graphql
   Host: api.tapjoy.com
   Authorization: Bearer <OAuth Token>

   {
     "query": "query { user { firstName } }"
   }
   ```

2. **Curl**

   ```curl

   curl -H "Authorization: Bearer <OAuth Token>" -X POST -d "\
   {\
     \"query\": \"query { user { firstName } }\"\
   }\
   " https://api.tapjoy.com/graphql
   ```

   ```ruby title="Ruby"

   require 'json'
   require 'net/https'

   access_token = "<OAuth Token>"

   query = <<~END
   query {
     user {
       firstName
     }
   }
   END
   json = JSON.dump({query: query})

   http = Net::HTTP.new('api.tapjoy.com', 443)
   http.use_ssl = true

   request = Net::HTTP::Post.new('/graphql')
   request['Authorization'] = "Bearer #{access_token}"
   request.body = json
   response = http.request(request)

   result = JSON.parse(response.body)
   data = result['data']
   errors = result['errors']
   ```

## 处理错误##working-with-errors

使用 API 时，可能会遇到以下几类错误：

* 服务器/网络错误
* 身份验证/授权错误
* 查询语法错误
* 数据验证错误

为了确保系统正确集成，需要考虑系统应如何处理这些类别的错误。

> **Note:**
>
> Reporting API 具有原子性。这意味着，如果在单个查询中执行多个操作，当其中的任何步骤失败时，整个查询都将失败。

下面描述的每个错误类别都将使用以下查询作为上下文：

```json
query {
  user {
    firstName
    lastName
  }
}
```

### 服务器和网络错误##server-and-network-errors

使用 API 时，偶尔可能会遇到无法恢复的服务器/网络错误。在此类情况下，您将收到与相关场景对应的正确 http 状态代码。例如，如果 API 中出现阻止执行查询的问题，那么您将收到一个包含 500 状态代码的 HTTP 响应。

在这种场景下，最好的处理方式是使用指数退避的重试策略。

### 身份验证和授权错误##authentication-and-authorization-errors

身份验证/授权错误可能由以下几种原因导致：

* 您的 OAuth 令牌到期
* 您的 OAuth 令牌无效
* 我们的底层身份验证系统出现了问题

在这些情况下，您会收到 HTTP 200 OK 响应，其中包含以下数据：

无法验证 Authentication 时，返回以下响应。

```json
{
  "errors": [
    {
      "message": "Authentication could not be verified. Please try again later.",
      "code": 403
    }
  ]
}
```

* `message` - 人类可读的错误描述
* `code` - 与 HTTP 状态代码相对应的 GraphQL 状态代码

在这种场景下，最好的处理方式是获取新的访问令牌并重试相应请求。

### 查询语法错误##query-syntax-errors

所有查询和变更都会经过预先验证以确认其语法正确并遵循所述的数据架构。如果提供的查询未能通过所有架构验证，您将收到 HTTP 200 OK 响应，其中包含以下数据：

当查询未能通过架构验证时，返回以下响应，包括受报错影响的位置和字段。

```json
{
  "errors": [
    {
      "message": "Field 'user' doesn't exist on type 'Query'",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "fields": [
        "query",
        "user"
      ]
    }
  ]
}
```

* `message` - 人类可读的错误描述
* `locations` - 出现语法错误的位置
* `fields` - 语法错误影响的字段

在这种场景下，最好的处理方式是参考 API 文档并在[交互式资源管理器](https://api.tapjoy.com/graphql/docs/explorer)中测试查询。

### 数据验证错误##data-validation-errors

即使提供的变更具有正确的结构和所有必填字段，仍可能因业务验证失败而导致提交失败。例如，假设广告主将广告出价更改为低于 AdSet 的最小值，如下所示：

以下突变会尝试将出价金额设置为低于允许的最小值。

```json
mutation {
  updateAdSet(input: {
    id: "00000000-0000-0000-0000-000000000000",
    bidding: {amount: 10000}
  }) {
    adSet {
      id
    }
  }
}
```

在这种场景下，您会收到 HTTP 200 OK 响应，其中包含以下数据：

```json
{
  "data": {
    "updateAdSet": null
  },
  "errors": [
    {
      "message": "Amount is below the minimum (20000 micros)",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "updateAdSet",
        "input",
        "bidding",
        "amount"
      ],
      "code": 422
    }
  ]
}
```

* `message` - 人类可读的错误描述
* `locations` - 出现验证错误的位置
* `path` - 对判定无效字段的完全限定引用
* `code` - 与 HTTP 状态代码相对应的 GraphQL 状态代码

在这种场景下，最好的处理方式是将错误消息以及相应字段（通过路径）一起呈现给用户。

## 局限性##limitations

Reporting API 具有一些保护措施，可防止过度或滥用 API 调用。如果您的集成超过现有限制，请联系 Tapjoy 客户经理或支持团队，了解如何优化集成或提高上限。

以下示例以广告主为例进行展示，但同样适用于发行商的集成场景。

### 数据留存##data-retention

Reporting API 可以访问过去两年的数据。

### 分页##pagination

在查询分页类型时，客户端必须：

* 提供 `first` 或 `last` 参数
* 在单个页面中不超过 100 个节点

如果超过最大值，则结果将被截断，因此，请求的第一个/最后一个参数将被忽略。例如：

```graphql
{
  advertiser {
    adSets(first: 50) {
      edges {
        node {
          id
          name

          ads(first: 50) {
            edges {
              node {
                id
                name
              }
            }
          }
        }
      }
    }
  }
}

```

在以上查询中，最多返回 50 个广告集，每个广告集最多包含 50 个广告。

为了对集合进行分页，必须提供 `after` 或 `before` 参数来控制页面的开头位置。例如：

```graphql
{
  advertiser {
    adSets(first: 50, after: "Mg==") {
      edges {
        node {
          id
          name
        }
      }

      pageInfo {
        endCursor
        hasNextPage
      }
    }
  }
}
```

在以上查询中，最多返回 50 个广告集，从光标位置“Mg==”开始。该位置根据从上一个查询返回的 endCursor 确定。当 hasNextPage 为 false 时，应认定分页已完成。

您可以访问 [GraphQL 官方网站](https://graphql.org/learn/pagination/#pagination-and-edges)了解有关分页的更多信息。

### 调用复杂性##call-complexity

为了通过架构验证，所有 Marketing API 调用不得超过 10,000 次“调用”。在这种情况下，一次“调用”是指对资源的一次请求。由于 GraphQL 允许将多次调用合并到单个查询中，因此在执行查询之前，API 会提前计算单个查询的调用总数。

调用次数大致相当于从结果中选择的资源数量（例如广告系列、广告集、广告服务、应用、洞察等）

考虑以上同一个示例：

```graphql
{
  advertiser {
    adSets(first: 50) {     # <= 50 calls
      edges {
        node {
          id
          name

          ads(first: 50) {  # <= 50 calls
            edges {
              node {
                id
                name
              }
            }
          }
        }
      }
    }
  }
}
```

在此示例中，客户端一次最多请求 50 个广告集；对于每个广告集，最多从广告集请求 50 个广告。

调用总次数的计算方法：

```text
50      = 50 ad sets
+
50 x 50 = 2500 ads
        = 2550 calls
```

目前，对于每小时的调用次数，没有速率限制。但是，这种情况未来可能会所有改变。

### 洞察复杂性##insights-complexity

在任何级别查询的报告洞察都会产生额外的复杂性成本，而上述基本计算中并未反映这一成本。每天查询需要在计算中额外增加 1 次调用。

考虑以下示例：

```graphql
{
  advertiser {
    # 50 calls
    adSets(first: 50) {
      edges {
        node {
          id
          name

          # 7 calls
          insights(timeRange: {from: "2024-03-01T00:00:00Z", until: "2024-03-08T00:00:00Z"}) {
            timestamps
            reports {
              country
              impressions
              conversions
              spend
            }
          }
        }
      }
    }
  }
}
```

在此示例中，客户端一次最多请求 50 个广告集；对于每个广告集，都会请求 7 天的报告数据，涵盖 3 个指标和 1 个细分段。

调用总次数的计算方法：

```text
50      = 50 ad sets
+
50 x 7  = 350 insights
        = 400 calls
```
