Back to Home

AI Posts Verification in X via Puppeteer

The article analyzes the problem of false successes of the AI agent in X: 83 DOM clicks vs 16 real posts. Describes the implementation of verification via page reload and DOM scan to extract tweet ID. After the fix, success rate reached 94%. The approach is applicable to any platforms with silent filters.

83 Fake AI Successes in X: How We Fix with Verification
Advertisement 728x90

Verifying AI Agent Posts on X: From 83 False Wins to 94% Real Success

An AI agent for automated comments on X reported 83 successful posts, but only 16 actually appeared. The gap came from incomplete verification: clicking Reply doesn't guarantee the content shows up on the platform. X's spam filters quietly drop posts without any alerts.

How Posting Works via Browser

The agent uses Puppeteer CDP to control a real browser. It opens the thread, enters the text, and clicks Reply. The first version of the function only checked the DOM event:

const button = await page.$('[data-testid="tweetButton"]');
if (button) {  
  const disabled = await button.evaluate(el =>     
     el.getAttribute('aria-disabled') === 'true'  
  );  
  if (!disabled) {    
    await button.click();
    return true; // <-- problem: early success  
  }
}
return false;

Returning true marked it as a success at the DOM level, without platform confirmation. The database piled up records, and the dashboard glowed green.

Google AdInline article slot

X doesn't signal rejections: the field clears, the URL stays the same. Rate limits or content policies kick in silently, leaving the browser under the illusion of success.

Issues with Post IDs

For tracking, the agent pulled the ID from page.url() after clicking:

const tweetId = page.url().split('/status/')[1];

X doesn't redirect to the new post—the URL remains the parent's. All 83 records got the same parent_tweet_id, hiding the issue behind seemingly valid data.

Google AdInline article slot

The problem surfaced manually via X analytics: 83 reported vs. 16 real replies.

Root Cause: Gap Between DOM and Platform

DOM: button.click() → success ✓
Browser: event fired → success ✓
X Platform: content dropped → ??? (silence)
Database: recorded as "posted" → success ✓
Dashboard: 83 engagements → great ✓
Reality: 16 actual posts

Monitoring stopped at the browser. Platform filters are a black box.

Solution: Post-Verification with Page Reload

The new verifyReplyPosted function waits for rendering, reloads the page, and scans the DOM for the text:

Google AdInline article slot
export async function verifyReplyPosted(  
  page: Page,  
  replyText: string,  
  parentTweetId: string,
): Promise<{ verified: boolean; replyTweetId: string | null }> {
  
  await new Promise(r => setTimeout(r, 3000));  
  await page.reload({ waitUntil: "domcontentloaded" });  
  await new Promise(r => setTimeout(r, 5000));
  
  await page.evaluate("window.scrollBy(0, 1000)");  
  await new Promise(r => setTimeout(r, 2000));
  
  const result = await page.evaluate(`    
     (function() {      
         var snippet = '${safe}'; // first 30 chars      
         var articles = document.querySelectorAll(        
           'article[data-testid="tweet"]'      
         );
         for (var i = 0; i < articles.length; i++) {        
             var textEl = articles[i].querySelector(          
                 '[data-testid="tweetText"]'        
             );    

             if (!textEl) continue;        
             var text = (textEl.innerText || '').slice(0, 60);

             if (text.indexOf(snippet.slice(0, 30)) !== -1) {

                 var links = articles[i].querySelectorAll(            
                   'a[href*="/status/"]'          
                 );          

                 return { verified: true, replyTweetId: realId };        
               }      
         }            

         return { verified: false, replyTweetId: null };    
     })()  
 `);    
  return result;
}
  • Reload ensures a fresh thread without cache.
  • Searches via article[data-testid="tweet"] and [data-testid="tweetText"].
  • ID pulled from href links inside the article.
  • Only verified: true gets logged to the database.

Why This Approach Works Anywhere

This isn't unique to X. Platforms (social media, forms, payments, deployments) quietly reject content.

Key verification principles:

  • Mimic a real user: reload + scroll for lazy loading.
  • Hunt for a marker (text snippet) in the target DOM.
  • Pull IDs from actual elements, not URLs or events.
  • Skip logging without proof.
  • Log failures in real time.

Key Takeaways

  • DOM events ≠ platform success: A click doesn't guarantee posting.
  • Silent drops: Spam filters operate without alerts to thwart workarounds.
  • DOM-scan verification: Reload + text/ID search.
  • Post-fix metrics: 94% verified success rate, failures visible instantly.
  • Broad use: Any automated posting/submission needs user-like checks.

After implementation, the agent tracks real posts, the dashboard shows the truth. The method scales to other platforms with similar filters.

— Editorial Team

Advertisement 728x90

Read Next