Use X’s official API to find posts, then render an image from the fields your app is allowed to use. Do not write a Playwright bot that opens X pages and screenshots them as a way to collect posts. X’s Automation rules warn against scripting its website, and its Terms say that crawling or scraping the Services without prior written consent is prohibited. The implementation below separates discovery (API) from rendering (your authorized display), so you can review the current Developer Agreement and display rules before launch.
Start with the policy decision
X’s official Automation rules, updated April 2026, say: “Use non-API-based forms of automation, such as scripting the >x< website. The use of these techniques may result in the permanent suspension of your account.” X’s Terms of Service also state: “crawling or scraping the Services in any form, for any purpose without our prior written consent is expressly prohibited”. A browser can technically load a post, but that capability is not authorization.
Design the bot around an approved use case: process URLs submitted by your users, search an authorized topic, or monitor a defined set of authors. If the service operates an automated account, review the separate labeling, consent and opt-out requirements. Avoid adding automatic replies, mentions or follows; unsolicited keyword-triggered replies and mentions are restricted by X’s automation guidance.
Choose what the bot will trigger on
User-submitted URL
A user supplies a post URL. Your service extracts the post ID, looks it up through the API, validates that the returned data is usable, and creates a card. This is the narrowest scope and easiest to explain to users.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware match#1 Best Overall
- Includes Raspberry Pi 5 with 2.4Ghz 64-bit quad-core CPU (8GB RAM)
- Includes 128GB Micro SD Card pre-loaded with 64-bit Raspberry Pi OS, USB MicroSD Card Reader
- CanaKit Turbine Black Case for the Raspberry Pi 5
- CanaKit Low Noise Bearing System Fan
- Mega Heat Sink - Black Anodized
Search query
Your worker periodically runs a supported search query for keywords, exact phrases, hashtags, mentions, URLs, language or content type. Recent Search is documented for the previous seven days and allows up to 100 posts per request. Full-Archive Search reaches back to March 2006 and is documented with up to 500 posts per request, but full-archive access is limited to pay-per-use and Enterprise customers. Treat both figures as documentation values that can change; check the developer console and current API reference.
Authorized source list
Store an allow-list of author IDs and fetch only those posts. This reduces accidental collection and makes consent, rate limits and deletion processing more manageable.
Set up API access safely
- Create the developer project and app required by the current X API documentation.
- Generate the app credentials and tokens needed for the search or lookup endpoint you are entitled to use.
- Put secrets in environment variables or a secret manager. Never commit them, place them in client-side JavaScript, or print authorization headers in logs.
- Record the API entitlement, rate limits, retention rules and billing shown in your developer console. Access and prices vary; the reviewed documentation does not establish a universal plan or approval for a particular account.
Find posts through the API
Use the endpoint and authentication scheme currently documented for your account. The example below keeps the base URL configurable rather than assuming a plan-specific route. It requests fields commonly needed for a visual card; remove fields your entitlement does not provide.
Rank #2
- Includes Raspberry Pi 5 with 2.4Ghz 64-bit quad-core CPU (4GB RAM)
- Includes 128GB Micro SD Card pre-loaded with 64-bit Raspberry Pi OS, USB MicroSD Card Reader
- CanaKit Turbine Black Case for the Raspberry Pi 5
- CanaKit Low Noise Bearing System Fan
- CanaKit Mega Heat Sink - Black Anodized
Python worker
import os
import requests
API_URL = os.environ["X_SEARCH_URL"] # Set to the current Recent or Full-Archive endpoint
TOKEN = os.environ["X_BEARER_TOKEN"]
QUERY = os.environ.get("X_QUERY", "from:example -is:retweet")
params = {
"query": QUERY,
"max_results": 10,
"tweet.fields": "id,text,author_id,created_at,public_metrics,attachments",
"expansions": "author_id,attachments.media_keys",
"user.fields": "name,username,profile_image_url",
"media.fields": "type,url,preview_image_url,alt_text",
}
response = requests.get(
API_URL,
params=params,
headers={"Authorization": f"Bearer {TOKEN}"},
timeout=30,
)
response.raise_for_status()
payload = response.json()
for post in payload.get("data", []):
print(post["id"], post.get("text", "").replace("\n", " "))
For a single URL, extract its post ID and use the current post lookup endpoint instead of search. Keep the raw response long enough to process it, then apply the retention policy your agreement requires.
Pagination and idempotency
Persist the query, time window and pagination token. Give each output a deterministic key such as post_id + template_version; a retry then updates the same job instead of creating duplicate images. Stop when the API returns no next token, when your time window is complete, or when the account’s rate limit requires a delay.
Render an image without automating X’s website
Build a local HTML card from the API fields you are permitted to display, then render that card in a controlled browser or image library. This produces a consistent visual record without loading X to collect the post. Before publishing or redistributing it, verify the current Developer Agreement and display guidance for attribution, branding, formatting changes, media rights and your intended audience.
Rank #3
- CanaKit Raspberry Pi 5 Essentials Starter Kit
Minimal card renderer
from html import escape
from pathlib import Path
from playwright.sync_api import sync_playwright
def make_card(post, author):
name = escape(author.get("name", "Unknown author"))
handle = escape(author.get("username", ""))
text = escape(post.get("text", "")).replace("\n", "<br>")
created = escape(post.get("created_at", ""))
return f"""<!doctype html>
<meta charset='utf-8'>
<style>
body {{ margin:0; background:#eef1f5; font-family:system-ui,sans-serif; }}
.card {{ width:720px; box-sizing:border-box; margin:32px; padding:28px;
border-radius:18px; background:white; color:#15202b; box-shadow:0 3px 16px #0002; }}
.author {{ font-weight:700; }} .handle, .date {{ color:#536471; }}
.text {{ margin-top:20px; font-size:26px; line-height:1.35; }}
</style>
<article class='card'>
<div class='author'>{name}</div>
<div class='handle'>@{handle}</div>
<div class='text'>{text}</div>
<div class='date'>{created}</div>
</article>"""
post = {"text": "Example text", "created_at": "2026-09-30T12:00:00Z"}
author = {"name": "Example author", "username": "example"}
html = make_card(post, author)
Path("card.html").write_text(html, encoding="utf-8")
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(viewport={"width": 800, "height": 500}, device_scale_factor=2)
page.set_content(html, wait_until="load")
page.locator(".card").screenshot(path="post.png")
browser.close()
Playwright supports viewport, full-page, element and in-memory screenshots. Those are appropriate for pages and content you are authorized to automate; they do not grant permission to script X.
Display, retention and deletion checks
- Keep required attribution and recognizable X branding where the current guidance requires them.
- Do not alter display formatting beyond what the rules permit. Document your template version so you can update every retained image if guidance changes.
- Store the post ID, author ID, source timestamp and image hash with each asset.
- Implement deletion handling. X’s developer guidance says deleted content should be removed within 24 hours; verify the binding rule that applies to your app and region before choosing longer retention.
- Provide an operator path to delete an image and its cached source data immediately.
Reliability and operating design
Rate limits and retries
Honor response headers and documented limits. Use exponential backoff for transient 429 and 5xx responses, with a maximum retry count. Do not retry authentication failures or malformed queries indefinitely.
Free tools Windows power users keep installed
One-click scans. No signup required.
Freshness and cache
Recent Search’s seven-day window means a delayed worker can miss older posts. Persist the last successful timestamp and overlap windows slightly, then deduplicate by post ID. Cache API responses only for the period allowed by your agreement.
Rank #4
- Includes Raspberry Pi 5 16GB with 2.4Ghz 64-bit quad-core CPU (16GB RAM)
- Includes 128GB Micro SD Card pre-loaded with 64-bit Raspberry Pi OS, USB MicroSD Card Reader
- CanaKit Turbine Black Case for the Raspberry Pi 5
- CanaKit Low Noise Bearing System Fan
- Mega Heat Sink - Black Anodized
Content safety
Escape text before inserting it into HTML, constrain image dimensions, and reject unexpectedly large media. Treat URLs, alt text and usernames as untrusted input. Keep the renderer offline from X unless you have separately confirmed that network access is permitted.
Public versus private output
A private internal archive and a public gallery can have different display and redistribution obligations. Review the exact use, audience and geography with the current developer terms before enabling sharing.
Common failures and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| 401 or 403 | Missing, expired or insufficient token | Regenerate credentials, check scopes and confirm the endpoint is included in your entitlement. |
| 400 invalid query | Unsupported operator or malformed syntax | Start with a simple keyword query, then add one documented operator at a time. |
| 429 | Rate limit exceeded | Read limit headers, back off, reduce polling and use pagination carefully. |
| No results | Seven-day window, strict filters or deleted/protected content | Check the time range, query spelling and access rights; do not assume the post still exists. |
| Broken card layout | Unescaped text, long URLs or missing media | Escape HTML, wrap long tokens with CSS, set fallbacks and test unusual Unicode. |
| Duplicate images | Retry created a second job | Use a deterministic idempotency key based on post ID and template version. |
| Stale or prohibited image | Post was deleted or display rules changed | Run deletion reconciliation, remove the asset promptly and re-check current guidance. |
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. It can capture an authorized page, but you still must not use it to script X’s website against X policy. For your own card URL or another permitted page, one GET request returns PNG, JPEG, WebP or PDF:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Best Value
- Includes Raspberry Pi 5 with 2.4Ghz 64-bit quad-core CPU (8GB RAM)
- Includes 32GB EVO+ Micro SD Card pre-loaded with 64-bit Pi OS, USB MicroSD Card Reader
- CanaKit Turbine Black Case for the Raspberry Pi 5
- CanaKit 45W PD Power Supply for the Raspberry Pi 5
- Display Cable - 6 foot (Supports up to 4K 60p)
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const data = Buffer.from(await res.arrayBuffer());
See the ScreenshotNeo documentation for parameters. Cookie banners, newsletter popups and chat widgets are removed before the shot; bot checks, blank pages and failed loads are not billed, and response headers identify the page verdict and billing status. Its MCP server includes take_screenshot, get_page_info and capture_pdf for AI agents. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
Before launch checklist
- Document the trigger and why the bot is authorized to process each post.
- Confirm API access, limits, fields, retention and billing in the current console.
- Review display, attribution, branding, deletion and redistribution rules for your exact output.
- Test pagination, retries, duplicate delivery, deleted posts, protected authors, Unicode and missing media.
- Keep a delete/reconciliation job and an audit trail without logging secrets.
Frequently Asked Questions
Can I use Playwright to open an X post and save a screenshot?
Only if X has specifically authorized that access for your use case. Playwright’s screenshot function is a technical capability, not permission to automate the X website.
Which search endpoint should a new bot use?
Use the Recent Search or Full-Archive Search endpoint available to your account and current plan. Recent Search covers seven days; full-archive availability and limits are restricted and can change.
Is an API-rendered card automatically safe to redistribute?
No. Constructing an image from API fields avoids page scraping, but attribution, branding, formatting, deletion and redistribution rules still apply to the exact design and audience.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




