X API reference

The X bookmarks endpoint, in full

GET /2/users/:id/bookmarks is the only documented way to read a person’s saved posts: OAuth 2.0 user context, one to a hundred results a page, newest-bookmarked first, billed per post returned.

Last verified Primary source: docs.x.com — bookmarks by user

The contract

PropertyValue
AuthenticationOAuth 2.0 user context, authorization code with PKCE. An app-only bearer token will not work.
Scopestweet.read, users.read, bookmark.read, plus offline.access if you want a refresh token
max_resultsMinimum 1, maximum 100, default 10
Paginationnext_token and previous_token arrive in meta; you send one back as pagination_token
OrderingMost recently bookmarked first. Not by post date.
BillingPer resource returned, at the Posts: Read rate

Two of those rows are worth a second look. The default page size is 10, not 100 — which is a kinder default than it appears, given that every result is billed. And the ordering is by when the post was bookmarked, not when it was posted, so the first result is the last thing the user saved even if that post is four years old. That ordering is what makes a one-result freshness check meaningful, and it costs half a cent.

the cheapest useful request
GET https://api.x.com/2/users/1234567890/bookmarks?max_results=1
Authorization: Bearer <user access token>

# → meta.result_count = 1, and data[0].id is the newest saved post.
# Billed: 1 post read.

Rate limits, and the finding that matters

Method and endpointPer appPer user
GET /2/users/:id/bookmarks180 / 15 min
GET /2/users/:id/bookmarks/folders50 / 15 min50 / 15 min
GET /2/users/:id/bookmarks/folders/:folder_id50 / 15 min50 / 15 min
POST /2/users/:id/bookmarks50 / 15 min
DELETE /2/users/:id/bookmarks/:tweet_id50 / 15 min
Note the dash in the per-app column on the read endpoint.

There is no per-app limit on bookmark reads. The limit is per user, enforced against that user’s own token, so one customer’s import cannot throttle another’s. Read throughput scales linearly with the number of customers, which is not something you can assume about most endpoints.

And the per-user ceiling is generous: 180 requests every 15 minutes, at up to 100 results each, is 18,000 bookmarks per quarter-hour for one person. A complete backfill of a 5,000-bookmark account fits inside a single window with room to spare. So capacity is not the constraint on a bookmarks product — what you are willing to spend is.

There is no second route

People reasonably assume the official X data archive can seed an import for free. It cannot: the archive you download from your account settings contains posts, likes, direct messages and follower lists, and there is no bookmarks file in it. This has been a gap for years and it was still a gap when this page was checked. The API is the only compliant way to read bookmarks, which means the first import always costs money.

The other apparent route is a browser extension that scrapes your logged-in session. That is how several free tools work, and it avoids API cost entirely because it never touches the API. It also relies on undocumented internal endpoints, breaks whenever X changes them, and violates the developer terms. It is a fine weekend hack and a poor foundation for anything anyone pays for.

What people get wrong

Trying an app-only bearer token. Bookmarks are private to the account that made them, so there is no app-context version of this endpoint. You need a token minted for the user, which means the authorization code flow with PKCE and the user clicking approve.

Storing the pagination cursor between runs. A bookmark list is mutable — every new save shifts it — so a token from yesterday points into a list that no longer looks the same. Keep a high-water-mark post ID instead.

Re-reading stored posts to check they still exist. Post content is immutable, so a post you have stored never needs fetching again. The one thing you do need to know — whether it has been deleted — comes from a free compliance job, not from paid reads.

We built the thing this describes

Sift reads your bookmarks once, sorts them into categories, and emails you what you saved. The rules on this page are the reason it costs what it costs — a one-result probe instead of a full page, one paid pass per UTC day, and never an expansion parameter.

Connect X