AI 에이전트에게 트위터 리더를 쥐여 주세요: Claude, ChatGPT, Codex용 도구 정의

업데이트
Claude, ChatGPT, Codex가 API 키 없이 공개 X 프로필, 게시물, 답글, 검색을 읽게 해 주는 복사해서 쓰는 도구 정의.
한마디로: 에이전트가 X를 읽으려면 네 가지가 필요합니다. 핸들이나 게시물 링크로 만들 수 있는 URL, 읽을 수 있는 JSON 또는 Markdown 본문, 분기할 수 있는 오류, 그리고 볼 수 있는 제한입니다. UnifAPI의
/tools/x/*엔드포인트는 로그인 없이, 계정 없이, API 키 없이 이 네 가지를 모두 무료로 제공하고 방문자별 횟수 제한이 적용되므로, 도구 정의는 HTTP GET 하나입니다. 아래에 Anthropic의 tool use, OpenAI의 function calling, 그리고 Claude Code와 Codex용 SKILL.md 정의를 실었고, 모델에 필요한 세 가지 규칙도 함께 담았습니다.X-RateLimit-Remaining을 읽을 것,Retry-After만큼 쉴 것, 결과가 컨텍스트 윈도에 바로 들어갈 때는 Markdown을 요청할 것.
모델은 이미 X를 읽는 법을 압니다. Claude나 ChatGPT에게 @NASA가 이번 주에 무엇을 올렸는지 물어보면 시도는 합니다. 웹 검색, x.com 가져오기, 어쩌면 학습 데이터에서 꺼낸 Nitter URL까지. 검색은 요약을 돌려주고, 가져오기는 JavaScript 껍데기를 돌려주고, Nitter 인스턴스는 사라졌습니다. 그러면 모델은 추측하거나 사과합니다.
해결책은 더 똑똑한 모델이 아니라, 모델이 구성할 수 있는 URL과 파싱할 수 있는 본문을 가진 도구입니다. 이 글이 그 도구이고, 사람들이 실제로 배포하는 세 가지 형식으로 담았습니다.
엔드포인트
GET 여덟 개, 모두 https://unifapi.com/tools/x/ 아래에 있고, 모두 /llms.txt에 캡처된 샘플과 함께 문서화되어 있습니다.
| 엔드포인트 | 파라미터 | 반환 |
|---|---|---|
profile | handle, posts (1–10), cursor | 프로필, 참여율, 최근 게시물 |
post | id (id 또는 URL) | 작성자, 미디어, 인용 게시물, 지표가 포함된 게시물 하나 |
media | handle, cursor | 계정의 사진과 동영상 게시물 |
replies | id, sort=top|recent, cursor | 게시물과 그 공개 답글 |
search | q, cursor | 최근 게시물. X 검색 연산자 사용 가능 |
trends | location 또는 woeid | 지금 트렌딩 중인 것 |
communities | q, cursor | 주제별 공개 X 커뮤니티 |
followers | handle, kind=followers|following | 계정을 팔로우하는 사람, 또는 계정이 팔로우하는 사람 |
모든 목록은 { items, hasMore, nextCursor }입니다. 모든 오류는 { error: { type, message } }입니다. 모든 응답에 X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset이 실리고, 429에는 Retry-After가 더해집니다. Markdown은 format=md를 붙이세요.
Anthropic tool use
endpoint enum을 가진 도구 하나가 모델에게는 도구 여덟 개보다 단순하고, 파라미터 이름이 API와 일치하므로 모델이 틀릴 수가 없습니다.
{
"name": "read_x",
"description": "Read public X (Twitter) data with no account: a profile and its recent posts, one post, an account's media, the replies under a post, a search over recent posts (X operators like from:, since:, min_faves:, filter:media work), trends by location, Communities by topic, or an account's followers/following. Free, rate-limited to 20 requests per 10 minutes; search costs 2. Returns JSON. Pass cursor from a previous result to page.",
"input_schema": {
"type": "object",
"properties": {
"endpoint": {
"type": "string",
"enum": [
"profile",
"post",
"media",
"replies",
"search",
"trends",
"communities",
"followers"
]
},
"handle": {
"type": "string",
"description": "X handle or profile URL (profile, media, followers)"
},
"id": { "type": "string", "description": "Post ID or x.com status URL (post, replies)" },
"q": { "type": "string", "description": "Search query or Communities topic" },
"location": {
"type": "string",
"description": "Trends location slug: worldwide, us, uk, japan, india, brazil, korea, germany, spain, mexico"
},
"sort": { "type": "string", "enum": ["top", "recent"] },
"kind": { "type": "string", "enum": ["followers", "following"] },
"cursor": { "type": "string" }
},
"required": ["endpoint"]
}
}
핸들러는 fetch 한 줄입니다.
async function readX(input: { endpoint: string } & Record<string, string | undefined>) {
const { endpoint, ...params } = input;
const qs = new URLSearchParams(Object.entries(params).filter(([, v]) => v) as [string, string][]);
const res = await fetch(`https://unifapi.com/tools/x/${endpoint}?${qs}`, {
headers: { Accept: "application/json" },
});
const body = await res.json();
if (res.status === 429)
return { error: body.error, retryAfterSeconds: Number(res.headers.get("Retry-After")) };
return { ...body, remaining: Number(res.headers.get("X-RateLimit-Remaining")) };
}
remaining을 모델에게 돌려주는 것은 의도적입니다. 조회가 3회 남았다는 것을 알면 Claude는 벽에 부딪히는 대신 페이지 넘기기를 멈추고 가진 것으로 답합니다.
OpenAI function calling
같은 모양을 OpenAI의 래퍼로 감싼 것입니다.
{
"type": "function",
"function": {
"name": "read_x",
"description": "Read public X (Twitter) data with no account. endpoint=profile|post|media|replies|search|trends|communities|followers. Free, 20 requests per 10 minutes. Returns JSON with { items, hasMore, nextCursor } for lists.",
"parameters": {
"type": "object",
"properties": {
"endpoint": {
"type": "string",
"enum": [
"profile",
"post",
"media",
"replies",
"search",
"trends",
"communities",
"followers"
]
},
"handle": { "type": "string" },
"id": { "type": "string" },
"q": { "type": "string" },
"location": { "type": "string" },
"sort": { "type": "string", "enum": ["top", "recent"] },
"kind": { "type": "string", "enum": ["followers", "following"] },
"cursor": { "type": "string" }
},
"required": ["endpoint"]
}
}
}
Claude Code와 Codex용 SKILL.md
코딩 에이전트에는 도구 정의가 필요 없습니다. URL 패턴이 존재한다는 사실을 알려주면 됩니다. 프로젝트 안의 스킬 파일이 그 역할을 합니다.
---
name: read-x
description: Read public X (Twitter) profiles, posts, replies, search, trends, communities, and followers as JSON or Markdown with no API key, via https://unifapi.com/tools/x/*. Use when a task needs what an account or post says on X.
---
# Reading X
All endpoints are GET https://unifapi.com/tools/x/<endpoint>. No key. Free limit 20 requests
per 10 minutes (search costs 2), printed in X-RateLimit-* headers; on 429 wait Retry-After.
- profile?handle=nasa profile + recent posts (+ posts=1-10, cursor)
- post?id=<id or x.com URL> one post
- media?handle=nasa photo/video posts
- replies?id=<id>&sort=top replies under a post (sort=recent for newest)
- search?q=from:nasa%20filter:media recent posts; X operators work
- trends?location=us trending now
- communities?q=ai X Communities
- followers?handle=nasa&kind=following
Add &format=md for Markdown when the result goes into context. Lists are { items, hasMore,
nextCursor }; pass cursor= to page. Errors are { error: { type, message } }.
Full reference with samples: https://unifapi.com/llms.txt
이 파일이 있으면 "@NASA가 이번 주에 올린 글을 확인하고 가장 인기 있는 게시물의 답글을 요약해 줘"는 에이전트가 직접 작성하는 curl 두 번이 됩니다.
모델을 위한 세 가지 규칙
- 페이지를 넘기기 전에 남은 횟수를 읽습니다.
X-RateLimit-Remaining은 모든 응답에 있습니다. 프로필 하나에 그 답글과 검색 한 번이면 20유닛 중 4유닛이고, 모든 것을 끝까지 페이지 넘기는 루프는 1분 만에 20유닛입니다. Retry-After만큼 쉬고, 절대 루프로 재시도하지 않습니다. 429 본문에는upgradeURL도 있습니다. 작업이 무료 티어보다 크면 그곳으로 가면 되고, 워크스페이스 키와 같은 필드 이름을 씁니다.- 읽기용 결과에는 Markdown을, 코드용 결과에는 JSON을 요청합니다.
format=md는 프로필과 게시물을 따옴표 오버헤드 없이 수백 토큰으로 렌더링합니다. JSON은 모델이 데이터를 필터링하거나 정렬하거나 다른 도구에 넘길 때를 위한 것입니다.
MCP
에이전트가 이미 MCP를 쓴다면, 종량제 X API는 UnifAPI MCP 서버에 같은 작업(프로필, 게시물, 검색, 답글, 팔로워 등)으로 올라가 있으며 워크스페이스 키 뒤에서 쓴 만큼 냅니다. 위의 무료 HTTP 엔드포인트는 아직 키가 없거나, 작업이 키까지는 필요하지 않은 몇 건의 읽기일 때를 위한 것입니다.
FAQ
Claude가 트위터를 읽을 수 있나요?
도구가 있으면 가능합니다. Claude는 x.com 자체를 불러올 수 없지만(페이지가 JavaScript 껍데기입니다), 위의 read_x 도구가 있으면 프로필, 게시물, 답글, 검색 결과를 JSON으로 읽고 그 위에서 추론합니다.
ChatGPT가 트윗을 읽을 수 있나요?
같은 답입니다. 위의 함수 정의를 커스텀 GPT나 Assistants 워크플로에 넣으면 됩니다. ChatGPT의 내장 브라우징은 x.com을 렌더링하지 못합니다.
API 키가 필요한가요?
아니요. 엔드포인트는 키 없이, 로그인 없이, 계정 없이 무료로 쓸 수 있으며, 방문자별 횟수 제한이 적용됩니다. 10분에 20회 조회이고, 검색은 2회로 계산됩니다. 키는 상한 없는 종량제 접근에만 필요합니다.
에이전트가 데이터로 할 수 있는 일에 제한이 있나요?
엔드포인트는 공개 데이터만 읽습니다. 보호된 계정은 존재 여부와 수치만 반환하고 게시물은 절대 반환하지 않습니다. 공개 콘텐츠에 대해 X의 약관이 허용하는 방식으로 데이터를 사용하세요.