# Dữ liệu X

Truy xuất hồ sơ X, tìm kiếm người dùng và bài đăng, đồng thời phân trang dòng thời gian và phản hồi.

Chọn thao tác cho dữ liệu bạn cần. Cả năm endpoint đều dùng POST và trả về JSON ở gốc, không có bao bọc `data` chung.

Tất cả ví dụ dùng `FELO_API_KEY` từ [tổng quan](https://openapi.felo.ai/vi/docs/data-api/guides/felo-overview.md). Các cuộc gọi sử dụng **số dư nền tảng**, không phải tín dụng Felo Pro. Các ví dụ phản hồi chỉ mang tính minh họa; nội dung thực tế và các trường tùy chọn sẽ thay đổi.

## Chọn một thao tác

| Nhu cầu | Endpoint |
|---|---|
| Hồ sơ cho các tên người dùng đã biết | `POST /api/v1/data/x/user/info` |
| Tìm người dùng theo truy vấn tìm kiếm | `POST /api/v1/data/x/user/search` |
| Bài đăng từ một người dùng | `POST /api/v1/data/x/user/tweets` |
| Bài đăng khớp với truy vấn | `POST /api/v1/data/x/tweet/search` |
| Phản hồi cho các bài đăng đã biết | `POST /api/v1/data/x/tweet/replies` |

## 1. Lấy hồ sơ người dùng

`POST /api/v1/data/x/user/info`

| Trường | Kiểu | Bắt buộc | Ý nghĩa |
|---|---|---|---|
| `usernames` | mảng chuỗi | Có | 1–100 tên người dùng; gửi handle không có `@` |

```bash
curl -sS --request POST "https://openapi.felo.ai/api/v1/data/x/user/info" \
  -H "Authorization: Bearer $FELO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"usernames":["OpenAI"]}'
```

Phản hồi chứa `users` và `total`. Mỗi người dùng bao gồm `user_id`, `username` và `display_name`; văn bản hồ sơ, số người theo dõi, xác minh và các siêu dữ liệu khác cũng có thể xuất hiện. Một lô có thể trả về ít hồ sơ hơn số lượng yêu cầu. Hãy khớp theo tên người dùng hoặc ID, không theo vị trí trong mảng.

```json
{"users":[{"user_id":"example-user-id","username":"example","display_name":"Example"}],"total":1}
```

## 2. Tìm kiếm người dùng

`POST /api/v1/data/x/user/search`

| Trường | Kiểu | Bắt buộc | Ý nghĩa |
|---|---|---|---|
| `query` | string | Có | 1–1.000 ký tự |
| `cursor` | string | Không | Con trỏ phân trang, tối đa 4.000 ký tự |

```bash
curl -sS --request POST "https://openapi.felo.ai/api/v1/data/x/user/search" \
  -H "Authorization: Bearer $FELO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"AI research"}'
```

Phản hồi chứa `users`, `total`, `has_next` và `next_cursor`. Endpoint này không có tham số `limit`.

```json
{"users":[],"total":0,"has_next":false,"next_cursor":null}
```

## 3. Lấy dòng thời gian của người dùng

`POST /api/v1/data/x/user/tweets`

| Trường | Kiểu | Bắt buộc | Ý nghĩa |
|---|---|---|---|
| `username` | string | Cần một định danh | Handle người dùng, tối đa 100 ký tự |
| `x_user_id` | string | Cần một định danh | ID người dùng, tối đa 100 ký tự; ưu tiên hơn `username` |
| `limit` | integer | Không | 1–1.000; cách triển khai hiện tại dùng ít nhất 20, mặc định 20 |
| `cursor` | string | Không | Con trỏ phân trang, tối đa 4.000 ký tự |
| `include_replies` | boolean | Không | Bao gồm phản hồi lồng nhau; mặc định `false` |

```bash
curl -sS --request POST "https://openapi.felo.ai/api/v1/data/x/user/tweets" \
  -H "Authorization: Bearer $FELO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username":"OpenAI","limit":20,"include_replies":false}'
```

Phản hồi chứa `tweets`, `total`, `has_next` và `next_cursor`. Đặt `limit` thấp hơn 20 không đảm bảo sẽ nhận ít mục hơn hoặc hóa đơn nhỏ hơn. Bao gồm phản hồi có thể làm tăng dữ liệu trả về và số lượng kết quả tính phí.

### Giới hạn số kết quả và phản hồi lồng nhau

`limit` là số mục tiêu cho các bài đăng cấp cao nhất của yêu cầu này, không phải số kết quả được bảo đảm. Cách triển khai hiện tại dùng mục tiêu ít nhất là 20: mục tiêu 20 sẽ đọc một trang upstream; mục tiêu lớn hơn tự động lấy tối đa 50 trang. Trước mỗi trang, hệ thống kiểm tra thời gian đã trôi qua và dừng lấy thêm trang khi vượt quá 60 giây. Kiểm tra này không đảm bảo toàn bộ yêu cầu HTTP hoàn tất trong vòng 60 giây.

Việc thu thập cũng dừng khi đạt mục tiêu, upstream không còn trang tiếp theo, một trang trống, hoặc con trỏ bị thiếu. Vì vậy một phản hồi thành công có thể chứa ít hơn `limit` bài đăng. Hãy dùng `has_next` và `next_cursor` để tiếp tục khi có.

Với `include_replies=true`, mỗi bài đăng được trả về nhận tối đa 5 trang bổ sung và nhiều nhất 100 phản hồi lồng nhau. Việc thu thập có thể dừng sớm hơn khi không còn trang tiếp theo, một trang trống, hoặc con trỏ không thể tiến tiếp. Các phản hồi lồng này nằm ngoài `limit` và `total` cấp cao nhất, nhưng có thể làm tăng số lượng kết quả tính phí.

Các `replies` lồng nhau không phải là một kho phản hồi hoàn chỉnh và không có một con trỏ tiếp tục riêng cho việc thu thập bổ sung này. Để phân trang phản hồi độc lập, hãy dùng thao tác Đọc phản hồi và con trỏ mà nó trả về. Đừng dùng lại con trỏ của bài đăng cấp cao nhất cho phản hồi.

## 4. Tìm kiếm bài đăng

`POST /api/v1/data/x/tweet/search`

| Trường | Kiểu | Bắt buộc | Ý nghĩa |
|---|---|---|---|
| `query` | string | Có | Văn bản tìm kiếm, 1–1.000 ký tự |
| `query_type` | string | Không | `Latest` (mặc định) hoặc `Top` |
| `limit` | integer | Không | 1–1.000; cách triển khai hiện tại dùng ít nhất 20, mặc định 20 |
| `cursor` | string | Không | Con trỏ phân trang, tối đa 4.000 ký tự |
| `since_time` | integer or string | Không | Thời gian bắt đầu; dùng giây Unix để có giá trị số rõ ràng |
| `until_time` | integer or string | Không | Thời gian kết thúc; dùng giây Unix, không phải mili giây |

```bash
curl -sS --request POST "https://openapi.felo.ai/api/v1/data/x/tweet/search" \
  -H "Authorization: Bearer $FELO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"retrieval augmented generation","query_type":"Latest","limit":20}'
```

Phản hồi chứa `tweets`, `total`, `has_next` và `next_cursor`. Đối tượng bài đăng bao gồm `id`, `content`, `author`, `created_at` và `metrics`; phản hồi và bài đăng được trích dẫn/chuyển tiếp có thể được lồng vào nhau. Hãy coi ID là chuỗi.

### Giới hạn số lượng kết quả

`limit` là mục tiêu, không phải sự bảo đảm. Cách triển khai hiện tại dùng mục tiêu ít nhất là 20: mục tiêu 20 sẽ đọc một trang upstream; mục tiêu lớn hơn tự động lấy tối đa 50 trang. Thời gian đã trôi qua được kiểm tra trước mỗi trang, và không lấy thêm trang nào sau khi vượt quá 60 giây. Một yêu cầu upstream đang xử lý vẫn có thể mất thời gian, vì vậy đây không phải là toàn bộ thời gian chờ HTTP.

Việc thu thập cũng dừng khi đạt mục tiêu, upstream không còn trang tiếp theo, một trang trống, hoặc con trỏ bị thiếu. Việc trả về ít hơn `limit` bài đăng không có nghĩa là yêu cầu thất bại hoặc rằng mọi bài đăng khớp đã được lấy. Hãy kiểm tra `has_next` và `next_cursor` trước khi tiếp tục.

```json
{
  "tweets": [{
    "id":"example-post-id",
    "content":"Example post content",
    "author":{"user_id":"example-user-id","username":"example","display_name":"Example"},
    "created_at":"2026-11-01T00:00:00Z",
    "metrics":{},
    "replies":[]
  }],
  "total":1,
  "has_next":false,
  "next_cursor":null
}
```

## 5. Đọc phản hồi

`POST /api/v1/data/x/tweet/replies`

| Trường | Kiểu | Bắt buộc | Ý nghĩa |
|---|---|---|---|
| `tweet_ids` | string or string array | Có | Một ID bài đăng hoặc một lô tối đa 20 ID |
| `cursor` | string | Không | Tối đa 4.000 ký tự; dùng với một ID bài đăng |
| `since_time` | integer | Không | Thời gian bắt đầu theo giây Unix |
| `until_time` | integer | Không | Thời gian kết thúc theo giây Unix |

Thay thế chỗ giữ chỗ bằng một ID bài đăng thực:

```bash
curl -sS --request POST "https://openapi.felo.ai/api/v1/data/x/tweet/replies" \
  -H "Authorization: Bearer $FELO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tweet_ids":["POST_ID"]}'
```

Không giống như dòng thời gian và tìm kiếm bài đăng, các phản hồi được nhóm dưới `results`. Mỗi mục có trạng thái phân trang riêng:

```json
{"results":[{"tweet_id":"POST_ID","replies":[],"total":0,"has_next":false,"next_cursor":null}]}
```

### Giới hạn phản hồi cho mỗi yêu cầu

Mỗi yêu cầu đọc một trang phản hồi upstream cho mỗi `tweet_id` và giữ tối đa 100 phản hồi; nó không tự động lấy mọi trang phản hồi. Trong một lô tối đa 20 ID, giới hạn 100 phản hồi áp dụng riêng cho từng mục `results`. Số lượng thực tế có thể thấp hơn.

`total` của mỗi mục là số phản hồi được trả về trong mục đó, không phải tổng số phản hồi đầy đủ của bài đăng. Để tiếp tục, dùng `next_cursor` của mục đó và chỉ gửi ID bài đăng đơn lẻ của nó trong yêu cầu tiếp theo. Không có tham số `limit` để tăng giới hạn mỗi yêu cầu.

## Phân trang và kiểm soát chi phí

`total` đếm mảng tương ứng trong phản hồi hiện tại: các bài đăng cấp cao nhất cho truy vấn bài đăng, hoặc các phản hồi trong từng mục kết quả cho truy vấn phản hồi. Nó không phải là số lượng khớp toàn cục, không phải tổng số phản hồi đầy đủ của bài đăng, cũng không phải số lượng tính phí cuối cùng.

1. Gửi yêu cầu đầu tiên không có `cursor`.
2. Đọc `has_next` và `next_cursor` từ phản hồi. Với phản hồi, đọc chúng trên từng mục kết quả.
3. Nếu còn trang khác và con trỏ không rỗng, gửi lại nguyên trạng cùng truy vấn đó. Với một lô phản hồi, tiếp tục từng bài đăng riêng biệt.
4. Dừng khi không còn trang tiếp theo, khi con trỏ lặp lại, hoặc khi giới hạn trang hay chi tiêu của bạn đã đạt.

Siêu dữ liệu phân trang phản ánh phản hồi upstream cuối cùng; bộ sưu tập trả về có thể bị cắt ở một giới hạn kết quả. Ngay cả `has_next=false` cũng không đảm bảo rằng toàn bộ lịch sử X hoặc phản hồi đã được lấy. Hiển thị số lượng thực tế trả về thay vì gắn nhãn một tập kết quả có giới hạn là toàn bộ dữ liệu có sẵn.

Mỗi trang thành công có thể phát sinh phí. Năm thao tác này được tính phí theo kết quả trả về, không phải phí cố định theo yêu cầu. Phản hồi lồng nhau và các bản ghi khác được trả về có thể ảnh hưởng đến số lượng bị tính phí; đừng ước tính chi phí chỉ dựa trên độ dài mảng cấp cao nhất. Kiểm tra [giá hiện tại](https://openapi.felo.ai/api/v1/data/prices) và mức sử dụng trong console. Các trường đối tượng đầy đủ có trong [lược đồ OpenAPI](https://openapi.felo.ai/api/v1/data/openapi.json).
