> For the complete documentation index, see [llms.txt](https://helps.ptengine.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://helps.ptengine.com/cn/developer/user-data-upload-api.md).

# 用户属性上传 API

通过 Ptengine API，您可以将用户属性信息批量上传到 Ptengine，用于后续的用户分群分析等功能。

本 API 基于 REST 架构构建，接受 JSON 格式的请求体，并返回 JSON 编码的响应。

***

## 1. 认证

### 获取 API Key

在 Ptengine 的「Integration（集成）」页面获取或新建 API Key。

{% hint style="warning" %}
API Key 按项目发放，每个项目需要单独创建。
{% endhint %}

### 保护 API Key

API Key 是机密信息，请勿在浏览器或移动应用等客户端代码中暴露。建议通过后端服务器发送请求，从**环境变量**或**密钥管理服务**中安全读取 Key。

所有 API 请求都需要在 HTTP 请求头中包含 API Key：

```
Authorization: Bearer PTENGINE_API_KEY
```

***

## 2. API 规格

| 项目      | 内容                                          |
| ------- | ------------------------------------------- |
| API URL | `https://api.ptengine.com/v1/user/profiles` |
| 协议      | HTTPS                                       |
| 请求方法    | POST                                        |
| 数据格式    | JSON                                        |

单次请求最多可同时上传 100 个用户及其属性数据。

***

## 3. 请求体

请求体包含以下两个字段：

| 字段名          | 数据类型      | 是否必填 | 说明                                             | 限制                         |
| ------------ | --------- | ---- | ---------------------------------------------- | -------------------------- |
| `field_name` | String\[] | 是    | 用户属性键名的数组，必须包含 `user_id`                       | 最多 50 个字段，每个属性名最长 50 个字符   |
| `values`     | Array\[]  | 是    | 包含用户属性值的二维数组，顺序必须与 `field_name` 对应。空值用 `""` 表示 | 最多 100 个用户，每个属性值最长 128 个字符 |

***

## 4. 更新策略

每个请求必须包含 `user_id`（用户唯一标识符）。Ptengine 通过 `user_id` 判断用户是否已存在，并按以下规则处理：

| 用户状态 | 属性状态 | 更新策略     |
| ---- | ---- | -------- |
| 存在   | 存在   | 更新已有的属性  |
| 存在   | 不存在  | 为该用户新建属性 |
| 不存在  | /    | 新建用户及其属性 |

***

## 5. 请求示例

假设你想上传以下用户属性：

| user\_id | watch\_conan | watch\_attack\_on\_titan |
| -------- | ------------ | ------------------------ |
| abcd123  | yes          | no                       |
| abcd456  | yes          | yes                      |
| abcd789  | no           | no                       |

请求体如下：

```json
{
  "field_name": [
    "user_id",
    "watch_conan",
    "watch_attack_on_titan"
  ],
  "values": [
    ["abcd123", "yes", "no"],
    ["abcd456", "yes", "yes"],
    ["abcd789", "no", "no"]
  ]
}
```

{% hint style="info" %}
`values` 的顺序必须与 `field_name` 的顺序一致。
{% endhint %}

***

## 6. 各语言代码示例

发送请求前，请将 `<API_KEY>` 替换为你的实际 API Key。

### Python

```python
import requests
import json

url = "https://api.ptengine.com/v1/user/profiles"

payload = json.dumps({
  "field_name": [
    "user_id",
    "watch_conan",
    "watch_attack_on_titan"
  ],
  "values": [
    ["abcd123", "yes", "no"],
    ["abcd456", "yes", "yes"],
    ["abcd789", "no", "no"]
  ]
})
headers = {
  'Content-Type': 'application/json',
  'Authorization': 'Bearer <API_KEY>'
}

response = requests.post(url, headers=headers, data=payload)
print(response.json())
```

### Node.js

```javascript
const axios = require('axios');
let data = JSON.stringify({
  "field_name": [
    "user_id",
    "watch_conan",
    "watch_attack_on_titan"
  ],
  "values": [
    ["abcd123", "yes", "no"],
    ["abcd456", "yes", "yes"],
    ["abcd789", "no", "no"]
  ]
});

let config = {
  method: 'post',
  url: 'https://api.ptengine.com/v1/user/profiles',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer <API_KEY>'
  },
  data: data
};

axios(config)
  .then(response => console.log(response.data))
  .catch(error => console.log(error));
```

### PHP

```php
<?php
$client = new Client();
$headers = [
  'Content-Type' => 'application/json',
  'Authorization' => 'Bearer <API_KEY>'
];
$body = '{
  "field_name": ["user_id", "watch_conan", "watch_attack_on_titan"],
  "values": [
    ["abcd123", "yes", "no"],
    ["abcd456", "yes", "yes"],
    ["abcd789", "no", "no"]
  ]
}';
$request = new Request('POST', 'https://api.ptengine.com/v1/user/profiles', $headers, $body);
$res = $client->sendAsync($request)->wait();
echo $res->getBody();
```

### Java

```java
OkHttpClient client = new OkHttpClient().newBuilder().build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType,
  "{\"field_name\":[\"user_id\",\"watch_conan\",\"watch_attack_on_titan\"],"
  + "\"values\":[[\"abcd123\",\"yes\",\"no\"],"
  + "[\"abcd456\",\"yes\",\"yes\"],"
  + "[\"abcd789\",\"no\",\"no\"]]}");
Request request = new Request.Builder()
  .url("https://api.ptengine.com/v1/user/profiles")
  .method("POST", body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Authorization", "Bearer <API_KEY>")
  .build();
Response response = client.newCall(request).execute();
```

### Go

```go
package main

import (
  "fmt"
  "strings"
  "net/http"
  "io/ioutil"
)

func main() {
  url := "https://api.ptengine.com/v1/user/profiles"
  payload := strings.NewReader(`{
    "field_name": ["user_id", "watch_conan", "watch_attack_on_titan"],
    "values": [
      ["abcd123", "yes", "no"],
      ["abcd456", "yes", "yes"],
      ["abcd789", "no", "no"]
    ]
  }`)

  req, _ := http.NewRequest("POST", url, payload)
  req.Header.Add("Content-Type", "application/json")
  req.Header.Add("Authorization", "Bearer <API_KEY>")

  res, _ := http.DefaultClient.Do(req)
  defer res.Body.Close()
  body, _ := ioutil.ReadAll(res.Body)
  fmt.Println(string(body))
}
```

***

## 7. 响应

### 成功

HTTP 状态码 `200 OK`

```json
{
  "msg": "Success",
  "timestamp": 1684155980
}
```

### 失败

HTTP 状态码根据错误类型不同而不同。

```json
{
  "msg": "The reason for the request failure",
  "timestamp": 1684155980
}
```

### 状态码参考

| HTTP 状态码           | 说明                                              |
| ------------------ | ----------------------------------------------- |
| 200 (OK)           | 请求成功                                            |
| 400 (Bad Request)  | 请求无法接受。可能缺少必要参数（例如 `field_name` 中未包含 `user_id`） |
| 401 (Unauthorized) | API Key 无效或不正确                                  |
| 403 (Forbidden)    | API Key 没有执行此请求的适当权限                            |
| 404 (Not Found)    | 请求的资源不存在或 URL 不正确                               |
| 500/502/503/504    | Ptengine 服务器发生问题                                |

***

## 8. 大量数据上传

单次请求最多可上传 100 个用户。如果用户数超过 100，请分批发送，每批最多 100 个。

例如，要上传 600 个用户的数据，请拆分为 6 次请求，每次 100 个用户。
