Skip to content

Migrate Twitter adapter to official X API v2#191

Merged
user1303836 merged 2 commits intomainfrom
feature/twitter-v2-migration
Feb 6, 2026
Merged

Migrate Twitter adapter to official X API v2#191
user1303836 merged 2 commits intomainfrom
feature/twitter-v2-migration

Conversation

@user1303836
Copy link
Owner

@user1303836 user1303836 commented Feb 6, 2026

Summary

Replaces the third-party twitterapi.io proxy with the official X API v2 (pay-per-use). This eliminates the dependency on a third-party service and uses Twitter's native endpoints with Bearer Token authentication.

Key changes:

  • Rewrote TwitterAdapter to use X API v2 endpoints (/2/users/by/username/{username} and /2/users/{id}/tweets)
  • Switched auth from API key to Bearer Token (TWITTER_BEARER_TOKEN env var)
  • Added field expansions for rich tweet data (media, quoted tweets, long-form note_tweet)
  • In-memory user ID caching to minimize lookup API calls
  • Server-side filtering with exclude=retweets,replies and max_results=5

Changes

File Change
src/intelstream/adapters/twitter.py Full rewrite for X API v2
src/intelstream/config.py twitter_api_key -> twitter_bearer_token
src/intelstream/services/pipeline.py Updated adapter creation
src/intelstream/discord/cogs/source_management.py Updated config check
src/intelstream/adapters/__init__.py Added TwitterAdapter export
.env.example Updated Twitter config section
README.md Updated docs for X API v2
tests/test_adapters/test_twitter.py Full rewrite (21 tests)
tests/test_discord/test_source_management.py Updated field reference
tests/test_services/test_pipeline.py Updated field reference

Cost Profile

  • User lookup: ~1 credit per unique username (cached after first call)
  • Timeline fetch: ~5 credits per poll (5 tweets, server-side filtered)
  • Basic tier: $200/month for 15,000 reads -- sufficient for ~50 sources polled every 30 minutes

Architect Review Notes

Four issues found and fixed during code review:

  1. max_results was 10, changed to 5 (cost optimization per design)
  2. exclude was missing replies, changed to retweets,replies (server-side filtering)
  3. note_tweet field was missing from TWEET_FIELDS and _parse_tweet (long tweets >280 chars)
  4. adapters/__init__.py was not updated to export TwitterAdapter

Test Plan

  • All 595 tests pass
  • ruff check . passes
  • ruff format --check . passes
  • mypy src/ passes
  • User lookup and caching tested
  • Timeline parsing with media, quoted tweets, and note_tweet tested
  • Error handling for API errors, missing users, empty timelines tested
  • Skip-content mode tested
  • Manual test with real X API v2 Bearer Token
  • Verify cost is within expected range (~5 credits per poll)

Replace the third-party twitterapi.io proxy with direct X API v2
integration using Bearer Token authentication. Key changes:

- Rewrite TwitterAdapter to use GET /2/users/by/username and
  GET /2/users/{id}/tweets endpoints with field expansions
- Add in-memory user ID caching to minimize API credit usage
- Use server-side retweet filtering via exclude=retweets param
- Parse X API v2 response format (data/includes/meta structure)
- Extract quoted tweet text, media thumbnails, and author info
  from the includes expansion objects
- Rename config field twitter_api_key -> twitter_bearer_token
- Update pipeline, source management cog, and .env.example
- Rewrite test suite for v2 API format (21 tests, up from 17)
- Add tests for user ID caching, media thumbnails, empty
  timelines, and tweets without user expansion
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

…te_tweet test

- Fix README cost calculation (4,800 -> 28,800 reads/month for 10 sources at 15-min interval)
- Remove unused entities field from TWEET_FIELDS to reduce response payload
- Add explicit test for note_tweet long-form text preference over truncated text
@user1303836 user1303836 merged commit 5670d13 into main Feb 6, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant