Set X Article Cover Image API | Twitter Article Header
Set the cover image on an article you own, identified by its entity id. This attaches media you have already uploaded; it does not upload anything itself, so call media/upload first and pass the media_id it returns. Requires a registered session. Billed at $0.0016 per call. Cost: $0.0016 per call.
POST article/update_cover_media sets the cover image on an X article you own. It takes the article's base64 entity id and a media_id from media/upload, attaches that image as the article cover, and returns ok plus the updated article object with cover_media populated. It requires a registered session and costs $0.0016 per call. Uploading the image is a separate prior step.
Authorization
bearerAuth Pass your API key as a bearer token on every request: Authorization: Bearer <API_KEY>.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/article/update_cover_media" \ -H "Content-Type: application/json" \ -d '{ "id": "QXJ0aWNsZUVudGl0eToyMDg2NjUwMjI4OTUwNjgzNjQ4", "media_id": "2088949698573090816" }'{ "ok": true, "article": { "id": "QXJ0aWNsZUVudGl0eToyMDg2NjUwMjI4OTUwNjgzNjQ4", "rest_id": "2086650228950683648", "title": "Why we rebuilt our onboarding from scratch", "preview_text": "", "lifecycle": "Draft", "content_state": { "blocks": [], "entityMap": [] }, "cover_media": { "id": "QXBpTWVkaWE6...", "media_id": "2088949698573090816", "media_key": "3_2088949698573090816", "original_img_url": "https://pbs.twimg.com/media/....jpg", "original_img_width": 1600, "original_img_height": 900, "color_palette": [] }, "media_entities": [], "author": { "id": "44196397", "username": "elonmusk", "name": "Elon Musk" }, "created_at_secs": 1786400000, "modified_at_secs": 1786400300, "first_published_at_secs": null, "visibility_setting": null, "tweet_id": null, "public_url": null }}{ "error": "bad_request", "message": "Missing or malformed parameter. Fix the request before retrying."}{ "error": "unauthorized", "message": "The API key is missing, malformed, or revoked. Check the Authorization header."}{ "error": "insufficient_credits", "message": "Your balance is exhausted. Top up your credits to continue."}{ "error": "forbidden", "message": "The acting account is not authorized for this write action, or has no logged-in session."}{ "error": "not_found", "message": "The resource does not exist, for example a deleted tweet or a private account."}{ "error": "rate_limited", "message": "Too many requests. Back off and retry with exponential backoff."}{ "error": "server_error", "message": "Something failed on our side. Retry with backoff; if it persists, contact support."}Pricing
| Unit | Price |
|---|---|
| Per call | $0.0016 |
| Per 1,000 calls | $1.60 |
When to use
Use this after uploading an image with media/upload, when you want that image to be the article's header image. Reach for article/update_content instead if you want an image inside the article body.
Notes
- Upload the image first with media/upload and pass the media_id it returns. This endpoint attaches, it does not upload.
- Needs a session for the account you are writing as: either register one once (POST /customer/session), or pass per-call inline credentials as x-auth-token and x-ct0 request headers, which let a single API key act as many accounts. Returns 409 session_required if neither is supplied, 401 session_dead when the session has expired.
- Works on both a draft and an already-published article.
Related endpoints
- Create Article: Start a new draft article. No request body required.
- Update Article Title: Set the title on a draft or published article.
- Update Article Content: Replace an article's body with a new Draft.js content_state.
FAQ
Do I upload the image through this endpoint?
No. Upload it with media/upload first, then pass the returned media_id here. This endpoint only attaches an already-uploaded image as the cover.
What should I pass for media_category?
Nothing, in almost every case. It defaults to DraftTweetImage, which is what the X article editor itself sends for a cover image. It is exposed only so you are not blocked if X starts expecting a different category.
Can I set a cover on a published article?
Yes. It works on both draft and published articles, with no unpublish step needed.
How do I remove a cover image?
X does not expose a clear-cover operation through this path, so there is no way to unset a cover once one is attached. You can replace it by calling this endpoint again with a different media_id.
Delete Article POST
Delete an article you own, identified by its entity id. A Draft is hard-deleted directly. A Published article is unpublished first (reverting it to Draft) and then its announcement tweet is deleted, which is what makes it stop being publicly reachable, a two-step reversal for Publish Article. Pass lifecycle and tweet_id as optional fast-path hints when you already know them (from a prior list/create response); omitted, the server resolves them itself by scanning your Draft then Published lists, costing a couple of extra round trips. Requires a registered session. Billed at $0.0008 per call. Cost: $0.0008 per call.
Overview
Monitoring: Watch an X account for new posts and get them pushed to your own webhook, HMAC-signed, retried for 21 minutes on failure. Polls on a shared interval and never spends your read credits: monitor and webhook management are free account administration, not metered reads.