How to Quote Retweet via Twitter (X) API — Programmatic Guide
Quote-retweeting is the highest-signal engagement action on Twitter (X) — it puts a tweet in your followers' timeline with your own added context, unlike a plain retweet. Programmatic quote-retweets power scheduling tools, editorial workflows, and multi-account content amplification systems.
This guide walks the exact endpoint, the auth requirements (this is a write operation — different from read-only searches), rate-limit pacing that keeps you well below X's thresholds, and runnable Python with error handling.
Quote-retweet vs retweet vs reply
Retweet: shares the original tweet with no added text. In your followers' timeline as the original author + a small 'X retweeted' indicator. Endpoint: POST /twitter/retweet_tweet_v2 with login_cookies, tweet_id and proxy. There is no unretweet endpoint on twitterapi.io; undo a retweet in the X app.
Quote-retweet: shares the original + your added commentary above it. In your followers' timeline as your tweet with the quoted one embedded below. Endpoint: POST /twitter/create_tweet_v2 with tweet_text + attachment_url (the full URL of the quoted tweet), alongside login_cookies and proxy. (If you just want to quote a post by hand in the app or on x.com, the steps are in how to quote a tweet.)
Reply: your tweet appears in the thread under the original, not in your followers' timelines by default. Endpoint: POST /twitter/create_tweet_v2 with tweet_text + reply_to_tweet_id.
Quote-retweet is the strongest signal because it (a) shows the original tweet to your followers, (b) adds your context / opinion, (c) increments the quote-retweet counter on the original (visible + attributable).
For programmatic workflows: use retweet for pure amplification, quote-retweet for editorial/opinion amplification, reply for conversation.
Auth — this is a write op
Reads (search / user info / timeline) use X-API-Key header on twitterapi.io — simple.
Writes (create tweet / quote / retweet / delete) require user-context auth — the API needs to know which X account is posting. Two paths:
Write actions on twitterapi.io run through an authenticated session: you log the account in once with POST /twitter/user_login_v2 ($0.10 per login), passing your X username, email, password and a proxy you supply (a residential or ISP proxy works best), and it returns login_cookies. Store the returned login_cookies and reuse them until the session ends; do not log in on every run. Each post, delete, like, retweet, follow or unfollow is then one call at $0.0006 that takes login_cookies and the same proxy. The call acts as that X account, so X's rules apply to it and X can rate-limit, lock or suspend the account; keep a human pace. The session can stop working and require a new login. The proxy is required on every write call. Billing is per call that returns HTTP 200, and that includes some refusals from X, which come back as HTTP 200 with status: error. Reads are different: they need only an API key and no X account at all.
POST /twitter/create_tweet_v2 takes a JSON body with three required fields: login_cookies, tweet_text and proxy, plus your key in the X-API-Key header. Setting the optional attachment_url field to the full URL of a tweet (https://x.com/<user>/status/<id>) makes the new post a quote of it. A success response is {"status": "success", "message": "...", "tweet_id": "..."}; a refusal from X comes back as HTTP 200 with status: error and a message, so check status, not only the HTTP code.
X official OAuth 2.0: standard OAuth authorization code flow, get bearer token with tweet.write scope, use bearer on write calls. Per docs.x.com/fundamentals/authentication/overview.
For automation-friendly workflows, twitterapi.io's login-session pattern is typically simpler; for polish integration into an X-official app flow, use OAuth 2.0.
Runnable — quote-retweet with commentary
Single quote-retweet with error handling. login() runs once; keep the returned login_cookies (here in the X_LOGIN_COOKIES environment variable) and reuse it on later runs.
import os, requests
BASE = "https://api.twitterapi.io"
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
PROXY = os.environ["X_PROXY"] # a proxy you supply: http://username:password@ip:port
def login() -> str:
"""Log the account in once ($0.10) and return login_cookies. Store and reuse it."""
body = {
"user_name": os.environ["X_USERNAME"],
"email": os.environ["X_EMAIL"],
"password": os.environ["X_PASSWORD"],
"proxy": PROXY,
}
if os.environ.get("X_TOTP_SECRET"):
body["totp_secret"] = os.environ["X_TOTP_SECRET"]
r = requests.post(f"{BASE}/twitter/user_login_v2",
headers=HEADERS, json=body, timeout=120)
r.raise_for_status()
data = r.json()
if data.get("status") != "success":
raise RuntimeError(f"login failed: {data.get('message')}")
return data["login_cookies"]
def quote_retweet(login_cookies: str, quoted_tweet_link: str, commentary: str) -> dict:
"""Post a quote of one tweet through the logged-in session ($0.0006)."""
if len(commentary) > 280:
raise ValueError(f"commentary too long: {len(commentary)} > 280")
r = requests.post(
f"{BASE}/twitter/create_tweet_v2",
headers=HEADERS,
json={
"login_cookies": login_cookies,
"tweet_text": commentary,
"attachment_url": quoted_tweet_link, # full URL of the tweet being quoted
"proxy": PROXY,
},
timeout=60,
)
r.raise_for_status()
data = r.json()
if data.get("status") != "success": # X-side refusals arrive as HTTP 200
raise RuntimeError(f"quote failed: {data.get('message')}")
return data
cookies = os.environ.get("X_LOGIN_COOKIES") or login() # store and reuse the value
result = quote_retweet(
cookies,
quoted_tweet_link="https://x.com/OpenAI/status/1234567890",
commentary="Interesting — this changes how I'd approach the design tradeoff for large-context workloads.",
)
print(f"quote-retweet posted: tweet_id={result.get('tweet_id')}")Rate-limit safe bulk pattern
For workflows that quote-retweet many tweets (curation feeds, scheduled amplification), pace under 1 quote per 5 seconds per account to stay well below X's write thresholds. Multi-account distributes further.
Never burst: for x in list: quote_retweet(x) at machine speed will trip X's spam heuristics + suspend the account. Add explicit sleeps + jitter.
# Bulk quote-retweet with pacing + retry. Reuses one stored login session.
import os, requests, time, random
BASE = "https://api.twitterapi.io"
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
PROXY = os.environ["X_PROXY"] # a proxy you supply: http://username:password@ip:port
LOGIN_COOKIES = os.environ["X_LOGIN_COOKIES"] # saved from one user_login_v2 login (see above)
def quote_retweet(quoted_tweet_link: str, commentary: str, max_retries: int = 3) -> dict:
payload = {
"login_cookies": LOGIN_COOKIES,
"tweet_text": commentary,
"attachment_url": quoted_tweet_link,
"proxy": PROXY,
}
for attempt in range(max_retries):
try:
r = requests.post(f"{BASE}/twitter/create_tweet_v2",
headers=HEADERS, json=payload, timeout=60)
except requests.RequestException as e: # network problem: safe to retry
print(f" attempt {attempt+1} failed: {e}")
time.sleep(5 * (attempt + 1))
continue
if r.status_code == 429: # rate-limited
wait = 60 * (attempt + 1)
print(f" rate-limited, waiting {wait}s")
time.sleep(wait)
continue
r.raise_for_status()
data = r.json()
if data.get("status") != "success":
# X refused the post. The call is billed, so stop instead of retrying.
raise RuntimeError(f"quote refused: {data.get('message')}")
return data
raise RuntimeError("quote-retweet failed after retries")
BATCH = [
("https://x.com/a/status/111", "Insightful take on latency budgets."),
("https://x.com/b/status/222", "Contrarian view worth reading."),
("https://x.com/c/status/333", "This aligns with what we're seeing in production."),
]
for link, text in BATCH:
r = quote_retweet(link, text)
print(f" posted: {r.get('tweet_id')}")
time.sleep(5 + random.uniform(0, 3)) # 5-8s pacing per accountAlternative — X official /2/tweets POST
Same intent, official X endpoint: POST /2/tweets with JSON body { "text": "your commentary", "quote_tweet_id": "1234567890" } per docs.x.com/x-api/posts/manage-tweets/introduction.
Auth: OAuth 2.0 Authorization Code Flow with PKCE + tweet.write scope.
Rate-limit: POST /2/tweets is limited per user in 15-minute windows; the current figure is in docs.x.com/x-api/fundamentals/rate-limits.
Pricing per docs.x.com/x-api/getting-started/pricing: a quote post is billed as Post: Create, $0.015 per post, or $0.200 if the post contains a URL. Whether a quote counts as a post containing a URL is not stated in this guide; check X's pricing page before budgeting. A plain retweet is User Interaction: Create, $0.015.
Wins when: you're building a native X integration and need X-issued OAuth tokens. Loses when: you want a simpler login-session flow for automation, or a lower price per action.
Comparison — 2 quote-retweet paths
What can go wrong
Text > 280 chars: X rejects with 400. Client-side length check saves a round-trip.
Quoted URL malformed: attachment_url must be the full https://x.com/user/status/N form — shortened links won't resolve.
Quoted tweet deleted: post succeeds but the embed shows 'this tweet is unavailable'. Optionally pre-check the tweet exists with GET /twitter/tweets?tweet_ids=N before posting (an empty result means it is gone).
Account suspended for burst posting: pace at 5s+ per action, distribute across multiple accounts for scale.
Duplicate content: X blocks near-identical tweets from the same account. Vary commentary per post.
HTTP 200 with status: error: a refusal from X comes back this way and the call is still billed. Check the status field on every response and stop the batch on repeated errors.
Session ended: login_cookies can stop working when X ends the session. Log in again once and store the new value; do not log in on every run.
# Editorial workflow — cure top tweets from a list, quote-retweet with topic-tagged commentary.
import os, requests, time, random
BASE = "https://api.twitterapi.io"
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
PROXY = os.environ["X_PROXY"] # a proxy you supply: http://username:password@ip:port
LOGIN_COOKIES = os.environ["X_LOGIN_COOKIES"] # saved from one user_login_v2 login
def search_top(kw: str, min_faves: int = 500, limit: int = 10) -> list:
r = requests.get(
f"{BASE}/twitter/tweet/advanced_search",
headers=HEADERS,
params={"query": f"{kw} min_faves:{min_faves} -filter:retweets lang:en"},
timeout=15,
)
r.raise_for_status()
return r.json().get("tweets", [])[:limit]
def quote_retweet(link: str, commentary: str) -> dict:
r = requests.post(
f"{BASE}/twitter/create_tweet_v2",
headers=HEADERS,
json={"login_cookies": LOGIN_COOKIES, "tweet_text": commentary,
"attachment_url": link, "proxy": PROXY},
timeout=60,
)
r.raise_for_status()
data = r.json()
if data.get("status") != "success":
raise RuntimeError(f"quote failed: {data.get('message')}")
return data
TOPIC = "LLM evaluation"
COMMENTARY_TEMPLATE = "Sharing this — good {topic} thread worth reading in full."
top_tweets = search_top(TOPIC, min_faves=200, limit=5)
for t in top_tweets:
author = t.get("author", {}).get("userName")
tweet_id = t.get("id")
if not (author and tweet_id): continue
link = f"https://x.com/{author}/status/{tweet_id}"
commentary = COMMENTARY_TEMPLATE.format(topic=TOPIC)
result = quote_retweet(link, commentary)
print(f" quoted {author}/{tweet_id} → new tweet {result.get('tweet_id')}")
time.sleep(5 + random.uniform(0, 3))
# Cost per twitterapi.io/pricing: search reads at $0.00015/tweet, $0.0006 per quote post, $0.10 per loginQuestions readers ask
What's the maximum commentary length on a quote-retweet?
280 characters, same as any tweet. The embedded quoted tweet does not count against your 280 — it's rendered separately.
Can I quote-retweet a protected (locked) account's tweet?
Only if you follow that account AND you're posting from an account that also follows them. Public tweets from public accounts are quote-retweetable by anyone.
Does quote-retweeting notify the original author?
By default yes — the original author gets a notification (unless they've turned off notifications from you or muted your account). Consider this when doing high-volume quote workflows.
Can I delete a quote-retweet later?
Yes — same as any tweet. POST /twitter/delete_tweet_v2 with login_cookies, tweet_id and proxy (twitterapi.io) or DELETE /2/tweets/:id (X official). Deleting your quote does not delete the original.
How is a quote-retweet counted in engagement metrics?
The original tweet's quote-count increments by 1. Your quote-retweet itself accumulates its own favorite / retweet / reply counts independently. Two separate engagement records.
What happens if I try to quote my own tweet?
Fully supported — self-quotes are common for follow-ups or corrections. No special auth needed beyond the same login session (or the standard write scope on the official API) for your own account.
Can I schedule quote-retweets?
Both twitterapi.io and X official are immediate-post endpoints. For scheduling, wrap the call in your own cron / job scheduler with a target-time delay. See /blog/twitter-scheduler for scheduling patterns.
How much does a quote-retweet cost through the API?
On twitterapi.io, $0.0006 per POST /twitter/create_tweet_v2 call, plus $0.10 for the one POST /twitter/user_login_v2 login whose login_cookies you store and reuse. A plain retweet through POST /twitter/retweet_tweet_v2 is also $0.0006. On the official X API a quote post is Post: Create at $0.015 ($0.200 if the post contains a URL; check X's pricing page for whether a quote counts as one) and a retweet is User Interaction: Create at $0.015.
Continue
- twitterapi.io — pricing
- twitterapi.io docs — user_login_v2
- twitterapi.io docs — create_tweet_v2
- X API — pricing (docs.x.com)
- X — manage posts (quote_tweet_id field)
- X — rate limits reference
- X — OAuth 2.0 authentication
- Twitter (X) API — cluster hub
- Twitter (X) reply API programmatic tutorial
- Twitter (X) scheduler patterns
- Twitter (X) automation guide
- Twitter (X) API pricing
- twitterapi.io pricing
- How to quote a tweet in the X app and on the web
- 01How to Build an MCP Server in Python for Twitter (X) Data
- 02OpenClaw vs Claude Code: which agent should run your Twitter (X) jobs?
- 03Hermes Agent vs OpenClaw: Which Agent Fits Twitter (X) Work?
- 04Claude Code Channels: Telegram, Discord and Twitter (X) Alerts in Your Session
- 05Codex MCP: How to Add Any MCP Server to OpenAI Codex (CLI, IDE and App)
- 06How to Quote a Tweet (Quote Post) on X
- 07Count Tweets — Twitter (X) API Programmatic Guide
- 08Twitter (X) Search Filters — Programmatic API Guide
Stop reading. Start building.
Starter credits cover real testing on real data. Google sign-in, no card, no application queue.
Get an API keyAlready building with TwitterAPI.io? Publish an honest tutorial or build log about it and earn up to $200 a month in API credits.