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

# 소개

Powerstream은 공개 X 데이터에 액세스할 수 있는 가장 빠른 실시간 스트리밍 API입니다. 레거시 GNIP Powetrack API와 유사하게, 키워드, 연산자, 메타데이터를 기반으로 포스트를 필터링하기 위해 규칙을 사용합니다. Powerstream 엔드포인트에 지속적인 HTTP 연결이 맺어지면, 거의 실시간에 가깝게 일치하는 포스트를 수신할 수 있습니다.

현재 Powerstream은 최대 1,000개의 규칙을 지원하며, 각 규칙의 길이는 최대 2048자입니다.

<div id="key-features">
  ## 주요 기능:
</div>

* **실시간 데이터 제공**: 규칙과 일치하는 데이터를 거의 실시간으로 받아볼 수 있습니다.
* **정밀한 필터링**: 연산자가 포함된 Boolean 쿼리를 사용해 원하는 데이터만 정확하게 필터링합니다.
* **전송 방식**: HTTP/1.1 청크 전송 인코딩을 통한 JSON 응답.
* **로컬 데이터 센터 지원**: 복제 지연을 피해 지연 시간을 줄이기 위해 로컬 데이터 센터의 포스트만 가져옵니다.

<Note>
  Powerstream API는 일부 Enterprise 플랜에서 제공되는 프리미엄 서비스입니다.

  Powerstream 사용을 원하시거나 Enterprise 서비스에 대해 더 알고 싶으시다면, [Enterprise Request Form](/ko/forms/enterprise-api-interest)을 제출해 영업 팀에 문의해 주세요.
  Powerstream이 귀사의 요구사항을 어떻게 지원할 수 있는지 논의해 보겠습니다.
</Note>

<div id="quick-start">
  ## 빠른 시작
</div>

이 섹션에서는 Python과 `requests` 라이브러리를 사용해 PowerStream 엔드포인트를 빠르게 사용하기 시작하는 방법을 보여줍니다. `pip install requests`로 설치하세요. 모든 예제는 OAuth 2.0 Bearer 토큰 인증을 사용합니다. `YOUR_BEARER_TOKEN`을 실제 토큰으로 바꾸고, `os.getenv('BEARER_TOKEN')`와 같이 안전한 방법으로 보관하세요.

각 엔드포인트를 코드 스니펫과 함께 다룹니다. 파일 상단에 다음 import 문이 있다고 가정합니다:

```python theme={null}
import requests
import json
import time
import sys
import os  # 환경 변수용
```

<div id="setup">
  ### 설정
</div>

```python theme={null}
bearer_token = os.getenv('BEARER_TOKEN') or "YOUR_BEARER_TOKEN"  # 보안을 위해 환경 변수 사용
base_url = "https://api.x.com/2/powerstream"
rules_url = f"{base_url}/rules"  # 규칙 관리용
headers = {
   "Authorization": f"Bearer {bearer_token}",
   "Content-Type": "application/json"
}
```

<div id="1-create-rules-post-rules">
  ### 1. 규칙 생성 (POST /rules)
</div>

스트림을 필터링하는 규칙을 추가합니다.

```python theme={null}
data = {
   "rules": [
       {
           "value": "(cat OR dog) lang:en -is:retweet",
           "tag": "pet-monitor"
       },
       # 필요에 따라 규칙 추가 (최대 100개)
   ]
}

response = requests.post(rules_url, headers=headers, json=data)
if response.status_code == 201:
   rules_added = response.json().get("data", {}).get("rules", [])
   print("Rules added:")
   for rule in rules_added:
       print(f"ID: {rule['id']}, Value: {rule['value']}, Tag: {rule.get('tag', 'N/A')}")
else:
   print(f"Error {response.status_code}: {response.text}")
```

<div id="2-delete-rules-post-rules">
  ### 2. 규칙 삭제 (POST /rules)
</div>

ID(권장) 또는 값으로 규칙을 삭제합니다.

```python theme={null}
data = {
   "rules": [
       {
           "value": "(cat OR dog) lang:en -is:retweet",
           "tag": "pet-monitor"
       },
       # 필요에 따라 규칙 추가 (최대 100개)
   ]
}

response = requests.delete(rules_url, headers=headers, json=data)
if response.status_code == 200:
   deleted = response.json().get("data", {})
   print(f"Deleted count: {deleted.get('deleted', 'N/A')}")
   if 'not_deleted' in deleted:
       print("Not deleted:", deleted['not_deleted'])
else:
   print(f"Error {response.status_code}: {response.text}")
```

**팁**: 모든 규칙을 삭제하려면, 먼저 GET으로 규칙을 조회한 뒤 ID를 추출하여 일괄 삭제하세요.

<div id="3-get-rules-get-rules">
  ### 3. 규칙 조회 (GET /rules)
</div>

모든 활성 규칙을 조회합니다.

```python theme={null}
response = requests.get(rules_url, headers=headers)
if response.status_code == 200:
   rules = response.json().get("data", {}).get("rules", [])
   if rules:
       print("Active rules:")
       for rule in rules:
           print(f"ID: {rule['id']}, Value: {rule['value']}, Tag: {rule.get('tag', 'N/A')}")
   else:
       print("No active rules.")
else:
   print(f"Error {response.status_code}: {response.text}")
```

<div id="4-powerstream-get-stream">
  ### 4. PowerStream (GET /stream)
</div>

실시간 포스트를 위해 스트림에 연결합니다. 줄 단위로 읽으려면 `stream=True`를 사용하세요. 안정성을 높이기 위해 재연결 로직을 구현하세요.

```python theme={null}
stream_url = base_url

def main():
   while True:
       response = requests.request("GET", stream_url, headers=headers, stream=True)
       print(response.status_code)
       for response_line in response.iter_lines():
           if response_line:
               json_response = json.loads(response_line)
               print(json.dumps(json_response, indent=4, sort_keys=True))
               if response.status_code != 200:
                   print(response.headers)
                   raise Exception(
                       "Request returned an error: {} {}".format(
                           response.status_code, response.text
                       )
                   )
```

<div id="local-datacenter-support">
  #### 로컬 데이터센터 지원
</div>

지연 시간 최적화를 위해 Powerstream은 연결이 설정된 로컬 데이터센터에서 생성되었거나 발신된 포스트만 가져오는 옵션을 제공합니다. 이렇게 하면 복제 지연(replication lag)을 피할 수 있어, 다른 데이터센터에서 오는 포스트보다 더 빠르게 전달됩니다. 이를 활성화하려면 스트림 엔드포인트에 쿼리 매개변수 `?localDcOnly=true`를 추가합니다(예: `/2/powerstream?localDcOnly=true`). 연결된 데이터센터 정보는 스트림의 초기 데이터 페이로드와 응답의 HTTP 헤더 모두에 표시됩니다.

코드에서 사용하려면:

```python theme={null}
# 로컬 데이터센터 전용:
stream_url = "https://api.x.com/2/powerstream?localDcOnly=true"
```

`localDcOnly` 매개변수가 활성화된 경우, 스트림이 처음 연결될 때 사용 중인 로컬 데이터센터를 나타내는 다음과 같은 응답 헤더가 포함됩니다:

```bash theme={null}
'x-powerstream-datacenter': 'atla',
'x-powerstream-localdconly': 'true'
```

이와 함께 데이터센터를 지정하는 초기 페이로드도 전송합니다.

```bash theme={null}
{
    "type": "connection_metadata",
    "datacenter": "atla",
    "timestamp": 1762557264155
}
```

<Note>
  **팁:** 지연 시간을 최적화하려면 서로 다른 지리적 위치(예: 미국 동부 해안의 애틀랜타 근처 하나, 미국 서부 해안의 포틀랜드 근처 하나)에서 연결을 설정하고 각 연결에 대해 `localDcOnly=true`를 설정하세요. 이렇게 하면 각 데이터센터에서 나오는 게시물에 더 빠르게 접근할 수 있습니다. 여러분 쪽에서 여러 스트림을 집계해 데이터센터 간 데이터를 통합하세요.
</Note>

<div id="operators">
  ## 연산자
</div>

필터링 규칙을 설정할 때 키워드와 연산자를 함께 사용할 수 있습니다. 아래에서 사용 가능한 연산자 목록을 확인하세요.

<div id="field-based-operators">
  ### 필드 기반 연산자
</div>

<div id="user-operators">
  #### 사용자 연산자
</div>

| 연산자            | 요약              | 예시                                  |
| -------------- | --------------- | ----------------------------------- |
| `from:`        | 특정 사용자가 작성한 포스트 | `from:xdevelopers` 또는 `from:123456` |
| `to:`          | 특정 사용자에게 보낸 포스트 | `to:jvaleski`                       |
| `retweets_of:` | 특정 사용자의 리포스트    | `retweets_of:xdevelopers`           |

<div id="content-operators">
  #### 콘텐츠 연산자
</div>

| Operator        | Summary                     | Example                                  |
| --------------- | --------------------------- | ---------------------------------------- |
| `contains:`     | 특정 텍스트/키워드를 포함하는 포스트와 일치합니다 | `contains:hello` or `contains:-2345.432` |
| `url_contains:` | URL에 특정 텍스트가 포함된 포스트와 일치합니다 | `url_contains:"com/willplayforfood"`     |
| `lang:`         | 특정 언어의 포스트와 일치합니다           | `lang:en`                                |

<div id="entity-operators">
  #### 엔터티 연산자
</div>

| Operator | Summary                                                                                  | Example                                 |
| -------- | ---------------------------------------------------------------------------------------- | --------------------------------------- |
| `has:`   | 특정 엔터티를 포함하는 게시물을 검색합니다 (옵션: mentions, geo, links, media, lang, symbols, images, videos) | `has:images`, `has:geo`, `has:mentions` |
| `is:`    | 특정 유형이거나 특정 속성을 가진 게시물을 검색합니다 (옵션: retweet, reply)                                       | `is:retweet`, `is:reply`                |

<div id="location-operators">
  #### 위치 연산자
</div>

| Operator        | Summary                             | Example                                                                                  |
| --------------- | ----------------------------------- | ---------------------------------------------------------------------------------------- |
| `place:`        | 특정 장소/위치에서 작성된 포스트와 일치합니다           | `place:"Belmont Central"`, `place:02763fa2a7611cf3`                                      |
| `bounding_box:` | 지정한 지리적 경계 상자 내에서 작성된 포스트와 일치합니다    | `bounding_box:[-112.424083 42.355283 -112.409111 42.792311]`                             |
| `point_radius:` | 기준 지점으로부터 지정한 반경 내에서 작성된 포스트와 일치합니다 | `point_radius:[-111.464973 46.371179 25mi]`, `point_radius:[-111.464973 46.371179 15km]` |

<div id="advancedcontent-operators">
  #### 고급/콘텐츠 연산자
</div>

| Operator    | Summary                                               | Example |
| ----------- | ----------------------------------------------------- | ------- |
| `bio:`      | 특정 프로필 소개(bio) 내용을 가진 사용자가 작성한 포스트와 일치합니다 (구문 일치 사용)  | N/A     |
| `bio_name:` | 프로필 소개(bio)에 특정 이름이 있는 사용자가 작성한 포스트와 일치합니다 (구문 일치 사용) | N/A     |

<div id="additional-operators">
  #### 추가 연산자
</div>

| Operator                 | Summary           | Example                                     |
| ------------------------ | ----------------- | ------------------------------------------- |
| `retweets_of_status_id:` | 특정 포스트의 리포스트를 검색  | `retweets_of_status_id:1234567890123456789` |
| `in_reply_to_status_id:` | 특정 포스트에 대한 답글을 검색 | `in_reply_to_status_id:1234567890123456789` |

<div id="non-field-operators">
  ### 비필드 연산자
</div>

<div id="special-syntax-operators">
  #### 특수 구문 연산자
</div>

| 연산자                     | 요약             | 예시               |
| ----------------------- | -------------- | ---------------- |
| `@`                     | 멘션 연산자         | `@username`      |
| 구문 일치 (Phrase matching) | 정확히 일치하는 구문 검색 | `"exact phrase"` |

<div id="logical-operators">
  #### 논리 연산자
</div>

| 연산자       | 요약                | 예시                                    |
| --------- | ----------------- | ------------------------------------- |
| `OR`      | 표현식 사이의 논리 OR 연산  | `x OR facebook`                       |
| Space/AND | 표현식 사이의 논리 AND 연산 | `x facebook` (두 용어가 모두 포함되어야 함)       |
| `()`      | 복잡한 표현식의 그룹화      | `(x OR facebook) iphone`              |
| `-`       | 부정/제외             | `x -facebook` (x는 포함하지만 facebook은 제외) |

<div id="responses">
  ## 응답
</div>

Powerstream API의 페이로드는 레거시 GNIP PowerTrack API와 동일한 형식을 사용합니다. JSON 응답 예시는 다음과 같습니다:

```json theme={null}
[
   {
       "created_at": "Tue Mar 21 20:50:14 +0000 2006",
       "id": 20,
       "id_str": "20",
       "text": "just setting up my twttr",
       "truncated": false,
       "entities": {
           "hashtags": [],
           "symbols": [],
           "user_mentions": [],
           "urls": []
       },
       "source": "<a href=\"http://x.com\" rel=\"nofollow\">X Web Client</a>",
       "in_reply_to_status_id": null,
       "in_reply_to_status_id_str": null,
       "in_reply_to_user_id": null,
       "in_reply_to_user_id_str": null,
       "in_reply_to_screen_name": null,
       "user": {
           "id": 12,
           "id_str": "12",
           "name": "jack",
           "screen_name": "jack",
           "location": "",
           "description": "no state is the best state",
           "url": "https://t.co/ZEpOg6rn5L",
           "entities": {
               "url": {
                   "urls": [
                       {
                           "url": "https://t.co/ZEpOg6rn5L",
                           "expanded_url": "http://primal.net/jack",
                           "display_url": "primal.net/jack",
                           "indices": [
                               0,
                               23
                           ]
                       }
                   ]
               },
               "description": {
                   "urls": []
               }
           },
           "protected": false,
           "followers_count": 6427829,
           "friends_count": 3,
           "listed_count": 32968,
           "created_at": "Tue Mar 21 20:50:14 +0000 2006",
           "favourites_count": 36306,
           "utc_offset": null,
           "time_zone": null,
           "geo_enabled": true,
           "verified": false,
           "statuses_count": 30134,
           "lang": null,
           "contributors_enabled": false,
           "is_translator": false,
           "is_translation_enabled": false,
           "profile_background_color": "EBEBEB",
           "profile_background_image_url": "http://abs.twimg.com/images/themes/theme7/bg.gif",
           "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme7/bg.gif",
           "profile_background_tile": false,
           "profile_image_url": "http://pbs.twimg.com/profile_images/1661201415899951105/azNjKOSH_normal.jpg",
           "profile_image_url_https": "https://pbs.twimg.com/profile_images/1661201415899951105/azNjKOSH_normal.jpg",
           "profile_banner_url": "https://pbs.twimg.com/profile_banners/12/1742427520",
           "profile_link_color": "990000",
           "profile_sidebar_border_color": "DFDFDF",
           "profile_sidebar_fill_color": "F3F3F3",
           "profile_text_color": "333333",
           "profile_use_background_image": true,
           "has_extended_profile": true,
           "default_profile": false,
           "default_profile_image": false,
           "following": null,
           "follow_request_sent": null,
           "notifications": null,
           "translator_type": "regular",
           "withheld_in_countries": []
       },
       "geo": null,
       "coordinates": null,
       "place": null,
       "contributors": null,
       "is_quote_status": false,
       "retweet_count": 122086,
       "favorite_count": 263321,
       "favorited": false,
       "retweeted": false,
       "lang": "en"
   }
]
```

<div id="limits-best-practices">
  ## 한도 및 모범 사례
</div>

* 요청 한도: 규칙 관리에는 24시간 기준 50회 요청; 스트림에는 한도가 없지만 연결 한도는 적용됩니다.
* 재연결: 연결이 끊어질 경우 지수 백오프를 사용합니다.
* 모니터링: `Connection: keep-alive` 헤더를 사용합니다.
