DEV Community

Lautner
Lautner

Posted on • Originally published at chirpapi.fun

Auto-Like Tweets: A Developer's Guide to Automated Engagement

Auto-Like Tweets: A Developer's Guide to Automated Engagement

  June 18, 20266 min read

  Liking tweets is the simplest form of engagement on Twitter. It costs nothing, takes a second, and puts your name in front of the tweet author. Automating this process — liking tweets that match specific keywords — is one of the most effective growth tactics for new accounts. Here's how to build it.

  ## How It Works

  The flow is straightforward:


    - Search for tweets matching your target keywords

    - Filter out tweets you've already liked

    - Like each remaining tweet via the API

    - Log what you liked to avoid duplicates

    - Repeat on a schedule



  ## Python Implementation
Enter fullscreen mode Exit fullscreen mode
import requests
import time
import sqlite3

API_KEY = "YOUR_CHIRPAPI_KEY"
BASE_URL = "https://api.chirpapi.fun/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# Keywords to target
KEYWORDS = ["python programming", "web development", "startup"]

db = sqlite3.connect("engagement.db")
db.execute("""CREATE TABLE IF NOT EXISTS liked_tweets (
    tweet_id TEXT PRIMARY KEY,
    liked_at TEXT DEFAULT CURRENT_TIMESTAMP
)""")

def is_already_liked(tweet_id):
    row = db.execute(
        "SELECT 1 FROM liked_tweets WHERE tweet_id=?", (tweet_id,)
    ).fetchone()
    return row is not None

def search_and_like():
    for keyword in KEYWORDS:
        # Search for tweets
        search_resp = requests.get(
            f"{BASE_URL}/search",
            headers=HEADERS,
            params={"q": keyword, "count": 10},
            timeout=10
        )

        if search_resp.status_code != 200:
            print(f"Search failed for '{keyword}': {search_resp.status_code}")
            continue

        tweets = search_resp.json().get("tweets", [])
        print(f"Found {len(tweets)} tweets for '{keyword}'")

        for tweet in tweets:
            tweet_id = tweet["id"]

            if is_already_liked(tweet_id):
                continue

            # Like the tweet
            like_resp = requests.post(
                f"{BASE_URL}/like",
                headers=HEADERS,
                json={"tweet_id": tweet_id},
                timeout=10
            )

            if like_resp.status_code == 200:
                db.execute("INSERT OR IGNORE INTO liked_tweets (tweet_id) VALUES (?)", (tweet_id,))
                db.commit()
                print(f"Liked: {tweet.get('text', '')[:60]}...")
            elif like_resp.status_code == 429:
                print("Rate limited. Waiting 60 seconds...")
                time.sleep(60)
            else:
                print(f"Like failed: {like_resp.status_code}")

            # Random delay between likes (30-90 seconds)
            time.sleep(random.randint(30, 90))

search_and_like()
Enter fullscreen mode Exit fullscreen mode
  ## Node.js Implementation
Enter fullscreen mode Exit fullscreen mode
const fetch = require('node-fetch');
const sqlite3 = require('better-sqlite3');

const API_KEY = 'YOUR_CHIRPAPI_KEY';
const BASE = 'https://api.chirpapi.fun/v1';
const db = sqlite3('engagement.db');

db.exec(`CREATE TABLE IF NOT EXISTS liked_tweets (
  tweet_id TEXT PRIMARY KEY,
  liked_at TEXT DEFAULT CURRENT_TIMESTAMP
)`);

const KEYWORDS = ['javascript', 'react', 'webdev'];

async function searchAndLike() {
  for (const keyword of KEYWORDS) {
    const searchRes = await fetch(
      `${BASE}/search?q=${encodeURIComponent(keyword)}&count=10`,
      {headers: {'Authorization': `Bearer ${API_KEY}`}}
    );
    if (!searchRes.ok) continue;

    const {tweets} = await searchRes.json();

    for (const tweet of tweets) {
      const already = db.prepare(
        'SELECT 1 FROM liked_tweets WHERE tweet_id=?'
      ).get(tweet.id);
      if (already) continue;

      const likeRes = await fetch(`${BASE}/like`, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({tweet_id: tweet.id})
      });

      if (likeRes.ok) {
        db.prepare('INSERT OR IGNORE INTO liked_tweets VALUES (?)').run(tweet.id);
        console.log(`Liked: ${tweet.text.slice(0, 60)}...`);
      }

      // Wait 30-90 seconds between likes
      await new Promise(r => setTimeout(r, 30000 + Math.random() * 60000));
    }
  }
}

searchAndLike();
Enter fullscreen mode Exit fullscreen mode
  ## Rate Limit Handling

  Twitter enforces rate limits. ChirpAPI forwards these limits. Key rules:


    - **Likes:** ~300 per 15 minutes (official limit). Stay well under.

    - **Search:** ~180 per 15 minutes.

    - **Safe target:** 50-100 likes per hour for warm accounts, 20-30 for new accounts.



  Always handle 429 responses with exponential backoff:
Enter fullscreen mode Exit fullscreen mode
def like_with_backoff(tweet_id, max_retries=3):
    for attempt in range(max_retries):
        resp = requests.post(
            f"{BASE_URL}/like",
            headers=HEADERS,
            json={"tweet_id": tweet_id},
            timeout=10
        )
        if resp.status_code == 200:
            return True
        if resp.status_code == 429:
            wait = 60 * (2 ** attempt)  # 60s, 120s, 240s
            print(f"Rate limited. Waiting {wait}s...")
            time.sleep(wait)
        else:
            break
    return False
Enter fullscreen mode Exit fullscreen mode
  ## Safety: Account Warm-Up Schedule

  New accounts should not start with heavy automation. Twitter watches for sudden spikes in activity.


    - **Week 1:** 5-10 likes per day, spread across 8+ hours

    - **Week 2:** 15-25 likes per day

    - **Week 3:** 30-50 likes per day

    - **Week 4+:** 50-100 likes per day (if no warnings)



  If you receive a "your account looks automated" warning, stop all automation for 48 hours, then resume at half the previous rate.

  ## Keyword Selection Tips


    - **Be specific:** "python async" is better than "programming"

    - **Use quotes:** Search for exact phrases to reduce noise

    - **Exclude retweets:** Add `-filter:retweets` to your search query

    - **Target recent:** Add `since:2026-06-15` to avoid old tweets

    - **Filter by engagement:** `min_faves:5` to skip tweets nobody cares about
Enter fullscreen mode Exit fullscreen mode

Originally published at ChirpAPI Blog.

Top comments (0)