# X データ

X のプロフィールを取得し、ユーザーと投稿を検索し、利用可能な場合はタイムラインと返信をページ分割して取得します。

必要なデータに応じて操作を選択してください。5 つのエンドポイントはすべて POST を使用し、共通の `data` ラッパーなしで JSON をルートに返します。

すべての例は [概要](https://openapi.felo.ai/ja/docs/data-api/guides/felo-overview.md) の `FELO_API_KEY` を使用します。呼び出しには **プラットフォーム残高** が使われ、Felo Pro クレジットは使われません。レスポンス例は説明用であり、実際の内容や任意フィールドは変動します。

## 操作を選ぶ

| 必要なもの | エンドポイント |
|---|---|
| 既知のユーザー名のプロフィール | `POST /api/v1/data/x/user/info` |
| 検索クエリでユーザーを見つける | `POST /api/v1/data/x/user/search` |
| 1 人のユーザーの投稿 | `POST /api/v1/data/x/user/tweets` |
| クエリに一致する投稿 | `POST /api/v1/data/x/tweet/search` |
| 既知の投稿への返信 | `POST /api/v1/data/x/tweet/replies` |

## 1. ユーザープロフィールを取得する

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

| フィールド | 型 | 必須 | 意味 |
|---|---|---|---|
| `usernames` | string array | はい | 1〜100 件のユーザー名。`@` を付けずにハンドルを送信します |

```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"]}'
```

レスポンスには `users` と `total` が含まれます。各ユーザーには `user_id`、`username`、`display_name` が含まれ、プロフィール文、フォロワー数、認証情報、その他のメタデータも含まれる場合があります。バッチで返されるプロフィール数は要求数より少なくなる場合があります。配列位置ではなく、ユーザー名または ID で一致させてください。

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

## 2. ユーザーを検索する

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

| フィールド | 型 | 必須 | 意味 |
|---|---|---|---|
| `query` | string | はい | 1〜1,000 文字 |
| `cursor` | string | いいえ | ページ分割用カーソル、最大 4,000 文字 |

```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"}'
```

レスポンスには `users`、`total`、`has_next`、`next_cursor` が含まれます。この操作には `limit` パラメーターはありません。

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

## 3. ユーザーのタイムラインを取得する

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

| フィールド | 型 | 必須 | 意味 |
|---|---|---|---|
| `username` | string | いずれか 1 つの識別子が必須 | ユーザーハンドル、最大 100 文字 |
| `x_user_id` | string | いずれか 1 つの識別子が必須 | ユーザー ID、最大 100 文字。`username` より優先されます |
| `limit` | integer | いいえ | 1〜1,000。現在の実装では少なくとも 20 が使用され、既定値は 20 |
| `cursor` | string | いいえ | ページ分割用カーソル、最大 4,000 文字 |
| `include_replies` | boolean | いいえ | ネストされた返信を含める。既定は `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}'
```

レスポンスには `tweets`、`total`、`has_next`、`next_cursor` が含まれます。`limit` を 20 未満に設定しても、返される件数や請求額が少なくなるとは限りません。返信を含めると、返されるデータと課金対象結果が増える場合があります。

### 結果数とネストされた返信の上限

`limit` はこのリクエストで取得するトップレベル投稿のおおよその件数であり、保証された結果数ではありません。現在の実装では少なくとも 20 を目標に動作します。20 のターゲットでは上流の 1 ページを読み取り、それより大きいターゲットでは最大 50 ページまで自動で取得します。各ページの前に経過時間を確認し、60 秒を超えた時点でそれ以降のページ取得を停止します。この確認は、HTTP リクエスト全体が 60 秒以内に完了することを保証するものではありません。

取得は、目標件数に達した場合、上流に次のページがない場合、ページが空の場合、またはカーソルがない場合にも停止します。そのため、成功レスポンスに `limit` より少ない投稿しか含まれないことがあります。利用可能な場合は `has_next` と `next_cursor` を使って続行してください。

`include_replies=true` の場合、返された各投稿に最大 5 ページ分、合計最大 100 件のネストされた返信が追加されます。次のページがない、ページが空、またはカーソルを進められない場合は、より早く停止することがあります。これらのネストされた返信はトップレベルの `limit` と `total` の外にありますが、課金対象結果に含まれる場合があります。

ネストされた `replies` は完全な返信アーカイブではなく、この追加取得に対する別の継続カーソルも公開されません。返信を独立してページ分割するには、Read replies 操作とそこで返されるカーソルを使用してください。返信にトップレベル投稿のカーソルを再利用しないでください。

## 4. 投稿を検索する

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

| フィールド | 型 | 必須 | 意味 |
|---|---|---|---|
| `query` | string | はい | 検索テキスト、1〜1,000 文字 |
| `query_type` | string | いいえ | `Latest`（既定）または `Top` |
| `limit` | integer | いいえ | 1〜1,000。現在の実装では少なくとも 20 が使用され、既定値は 20 |
| `cursor` | string | いいえ | ページ分割用カーソル、最大 4,000 文字 |
| `since_time` | integer or string | いいえ | 開始時刻。あいまいさのない数値には Unix 秒を使用してください |
| `until_time` | integer or string | いいえ | 終了時刻。ミリ秒ではなく Unix 秒を使用してください |

```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}'
```

レスポンスには `tweets`、`total`、`has_next`、`next_cursor` が含まれます。投稿オブジェクトには `id`、`content`、`author`、`created_at`、`metrics` が含まれ、返信や引用/リポストされた投稿がネストされる場合があります。ID は文字列として扱ってください。

### 結果件数の上限

`limit` は保証ではなく目標値です。現在の実装では少なくとも 20 を目標に動作します。20 のターゲットでは上流の 1 ページを読み取り、それより大きいターゲットでは最大 50 ページまで自動で取得します。各ページの前に経過時間を確認し、60 秒を超えた時点でそれ以降のページは取得しません。進行中の上流リクエストには時間がかかることがあるため、これは HTTP 全体のタイムアウトではありません。

取得は、目標件数に達した場合、上流に次のページがない場合、ページが空の場合、またはカーソルがない場合にも停止します。`limit` より少ない投稿しか返らなくても、リクエストが失敗したことや、すべての一致投稿を取得したことを意味するわけではありません。続行する前に `has_next` と `next_cursor` を確認してください。

```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. 返信を読み取る

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

| フィールド | 型 | 必須 | 意味 |
|---|---|---|---|
| `tweet_ids` | string or string array | はい | 1 件の投稿 ID、または最大 20 個の ID のバッチ |
| `cursor` | string | いいえ | 最大 4,000 文字。1 件の投稿 ID と一緒に使用します |
| `since_time` | integer | いいえ | Unix 秒による開始時刻 |
| `until_time` | integer | いいえ | Unix 秒による終了時刻 |

プレースホルダーを実際の投稿 ID に置き換えてください:

```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"]}'
```

タイムラインや投稿検索とは異なり、返信は `results` の下にグループ化されます。各エントリには独自のページ分割状態があります:

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

### リクエストごとの返信上限

各リクエストは `tweet_id` ごとに 1 つの上流返信ページを読み取り、最大 100 件の返信を保持します。すべての返信ページを自動取得するわけではありません。最大 20 個の ID のバッチでは、100 件の上限は各 `results` エントリに個別に適用されます。実際の件数はこれより少ない場合があります。

各エントリの `total` は、そのエントリで返された返信数であり、投稿の完全な返信数ではありません。続行するには、そのエントリの `next_cursor` を使用し、次のリクエストにはその 1 件の投稿 ID のみを送信してください。1 リクエストあたりの上限を増やす `limit` パラメーターはありません。

## ページ分割とコスト制御

`total` は、現在のレスポンス内の対応する配列の件数を数えます。投稿クエリではトップレベル投稿、返信クエリでは各結果エントリ内の返信です。これは、グローバルな一致件数、投稿の完全な返信数、または最終的な課金対象件数ではありません。

1. 最初のリクエストは `cursor` なしで送信します。
2. レスポンスから `has_next` と `next_cursor` を読み取ります。返信については、各結果エントリで読み取ります。
3. さらにページがあり、カーソルが空でない場合は、同じクエリでそのまま送信します。返信のバッチでは、各投稿を個別に続行します。
4. 次のページがない場合、カーソルが繰り返される場合、または自分のページ数や支出上限に達した場合は停止します。

ページ分割のメタデータは最後の上流レスポンスを反映しており、返されたコレクションは結果上限で切り詰められる場合があります。`has_next=false` であっても、すべての X の履歴や返信を取得したことを保証するものではありません。利用可能なデータがすべてであるかのように、制限付きの結果セットを表示しないでください。

成功した各ページには課金が発生する場合があります。これら 5 つの操作は、フラットなリクエスト शुल्कではなく返された結果に基づいて課金されます。ネストされた返信やその他の返されたレコードは課金対象件数に影響する場合があります。トップレベル配列の長さだけでコストを見積もらないでください。[現在の価格](https://openapi.felo.ai/api/v1/data/prices) とコンソールでの使用状況を確認してください。完全なオブジェクトフィールドは [OpenAPI スキーマ](https://openapi.felo.ai/api/v1/data/openapi.json) にあります。
