How to search tweets by date and archive an account's tweets
How to search tweets by date with X's since: and until: operators, real advanced search examples, and an API script that archives any account's tweets to JSON.
Piyushh · October 14, 2026 · 8 min read
To search tweets by date on X, add since: and until: to any search, for example from:nasa since:2024-04-01 until:2024-04-15, or use the Dates fields in Advanced search. That works for one-off lookups. Archiving an account's tweets is a different job. You want every tweet in a date range saved as data, and that takes an API and a loop.
This guide covers both. The first part shows how to search tweets by date in the X app: which operators do the filtering and which ones X's own API rejects. The second part is a working script that pulls an account's tweets into JSON, plus a way to reach dates the timeline won't page back to.
How to search tweets by date in X search
X's advanced search help page (checked 2026-10-14) walks through the form: search, click Advanced search under Search filters (or More options), fill in the fields, and run the search. In the Dates section you can set a "from" date, a "to" date or both, and X says you can search "for posts from any date since the first public post."
The help page covers the form but not the operators you can type. X's developer docs confirm that web search uses the names min_faves: and min_retweets: (operator docs, checked 2026-10-14). Everything else below is long-standing X search syntax. Typing operators is faster than filling in the form:
| Operator | What it does | Example |
|---|---|---|
since:YYYY-MM-DD | Tweets on or after a date | launch since:2026-09-01 |
until:YYYY-MM-DD | Tweets before a date | launch until:2026-10-01 |
from:handle | Tweets by one account | from:levelsio |
to:handle | Replies sent to one account | to:XDevelopers |
min_faves:N | At least N likes | min_faves:500 |
min_retweets:N | At least N reposts | min_retweets:100 |
min_replies:N | At least N replies | min_replies:20 |
filter:links | Only tweets with links | filter:links |
-filter:replies | Drop replies | -filter:replies |
lang:xx | One language | lang:en |
On until:: treat the end of a range as fuzzy. For all of September, set until:2026-10-01 and check each tweet's timestamp in your own code instead of trusting the boundary to the minute. API timestamps (createdAt) are in UTC, so they may not match the day you saw on your screen.
Search tweets by date: worked examples
- Everything one account posted in a two-week window:
from:nasa since:2024-04-01 until:2024-04-15 - Replies people sent a brand during an outage:
to:github since:2026-06-10 until:2026-06-12 - Popular tweets on a topic in a month:
"cold email" min_faves:200 since:2026-09-01 until:2026-10-01 - Your own old tweets about a subject:
from:yourhandle "conference" until:2020-01-01 - Original tweets with links only:
from:levelsio filter:links -filter:replies since:2026-01-01
Growth people mine ideas with the same operators. @LoganTGott's recipe: "Type a general topic followed by min_faves:1000", click the Top tab, and "Every result is a post with 1,000+ likes" (post, October 2026).
Why X search misses tweets you know exist
X search does not index every tweet, and nothing on the search box tells you that. In r/Twitter, u/Wanginmang guessed that "old and no any engagement" posts are left out of search, and u/apokrif1 noticed "You may get more results in "top" than in "latest"!" (thread). The original poster later said search came back after a few months, which points to a glitch rather than a policy. In August 2026 another user wrote that search had been "completely broken for well over a month" while they were trying to find old tweets "for archival purposes" (thread).
These are user reports, not measurements. They matter because any tool built on X search, an API's search endpoint included, inherits whatever X search returns. A search endpoint can't return a tweet that X search is missing. For your own account, there is a better source.
Your own tweet archive: the complete record
X lets you download everything you've posted. The archive help page (checked 2026-10-14) gives the steps in the app: Settings and privacy, Account, Your X data, verify your identity with a code, then Request data next to X. When the .zip is ready, X emails you a link, and it warns this "may take a few days." The archive holds your posts, DMs, media, followers and following as HTML and JSON files.
Since search started misbehaving, people have been turning the archive into their own search engine. Ken Wheeler suggested you "download your whole twitter archive and have the clankers build a search that works and find any tweet of yours ever like basically instantly" (post). @ZyMazza did the same: "twitter search is still broken, but you can download your twitter archive, point hermes at it, and then find any tweet you want" (post).
This only covers your own account. To get a tweet archive for anyone else, or to search archives across many accounts, you need an API.
Archive any account's tweets with an API
Two ChatterAPI endpoints cover this. Both are read-only and both run on the same key:
x.tweets(GET /v1/x/users/{handle}/tweets) pages through one account's recent tweets, newest first, at 3 credits per page of about 20. The free X timeline scraper makes the same call from a browser.x.search(GET /v1/x/tweets/search) accepts any X operators inq, plussinceanduntilparameters inYYYY-MM-DD,sort(latestortop),langandincludeRetweets. It costs 3 credits per page of about 20. You can try it without code in the free X tweet search.
Neither needs an X developer account. There's no key to apply for and no OAuth app.
Search tweets by date over the API
This is a date-bounded search. The response below is real, recorded with a test key, which returns one sample per endpoint for free. We trimmed it to three rows and the useful fields.
curl -G "https://api.chatterapi.com/v1/x/tweets/search" \
-H "Authorization: Bearer $CHATTERAPI_KEY" \
--data-urlencode 'q=crm recommendation' \
--data-urlencode 'until=2026-10-01'
{
"data": [
{
"id": "2099495333231288346",
"text": "meta ads: how important is the “connect to your crm” recommendation? ...",
"url": "https://x.com/HesomParhizkar/status/2099495333231288346",
"createdAt": "2026-09-14T13:47:48.000Z",
"likes": 2,
"replies": 2,
"views": 138,
"isReply": false,
"author": { "handle": "HesomParhizkar", "followers": 584 }
},
{
"id": "2040879100261458222",
"text": "Here's a live audit on what happens when buyers ask AI for a CRM recommendation. ...",
"createdAt": "2026-04-05T19:47:49.000Z",
"likes": 4,
"author": { "handle": "apoorvshrm", "followers": 2316 }
},
{
"id": "2030294157340860574",
"text": "Biggest thing I learned building LeadSignal: ...",
"createdAt": "2026-03-07T14:47:02.000Z",
"likes": 3,
"author": { "handle": "mohitbajoriaa", "followers": 531 }
}
],
"page": { "nextCursor": "AcWNIo1WFWYR…", "hasMore": true },
"credits": { "charged": 0, "remaining": 719 },
"requestId": "req_01M4X1JQMHXQYRQW06M8",
"cached": true
}
The sample page held 20 tweets, newest first, going back to 2019. A live key charges 3 credits for it, and the test key shows charged: 0. To get the next 20, pass page.nextCursor back as cursor. The operators can go in q (from:levelsio since:2026-09-01), or you can use the since and until parameters. Either works. Pick one and stay consistent.
A Python script that archives an account by date
This script pulls every tweet from one account in a date range and writes JSON lines. The main block walks the range in monthly windows with x.search and from:, and timeline() is there for when you only need the recent stretch from x.tweets. Windows are the default because timelines don't page back forever: X's own API docs say its user timeline endpoint returns "up to 3,200 most recent Posts" (X docs, checked 2026-10-14). A search by date window doesn't depend on how many tweets came after.
import json, os, datetime as dt
import requests
API = "https://api.chatterapi.com/v1"
H = {"Authorization": f"Bearer {os.environ['CHATTERAPI_KEY']}"}
def get(path, **params):
r = requests.get(f"{API}{path}", headers=H, params=params, timeout=60)
r.raise_for_status()
return r.json()
def timeline(handle, since, max_pages=50):
"""Newest-first pages from x.tweets until we pass `since`."""
cursor, pages = None, 0
while pages < max_pages:
body = get(f"/x/users/{handle}/tweets", includeRetweets="false",
**({"cursor": cursor} if cursor else {}))
pages += 1
rows = body["data"]
yield from (t for t in rows if (t["createdAt"] or "") >= since)
oldest = min((t["createdAt"] or "" for t in rows), default="")
print(f"page {pages}: {len(rows)} tweets, oldest {oldest[:10]}, "
f"credits left {body['credits']['remaining']}")
if oldest < since or not body["page"]["hasMore"]:
return
cursor = body["page"]["nextCursor"]
def window(handle, start, end, max_pages=20):
"""Every tweet by `handle` between two dates, via x.search."""
cursor = None
for _ in range(max_pages):
body = get("/x/tweets/search", q=f"from:{handle}", since=start, until=end,
includeRetweets="false", **({"cursor": cursor} if cursor else {}))
yield from body["data"]
if not body["page"]["hasMore"]:
return
cursor = body["page"]["nextCursor"]
def months(start, end):
d = dt.date.fromisoformat(start)
stop = dt.date.fromisoformat(end)
while d < stop:
nxt = (d.replace(day=1) + dt.timedelta(days=32)).replace(day=1)
yield d.isoformat(), min(nxt, stop).isoformat()
d = nxt
if __name__ == "__main__":
handle, since, until = "levelsio", "2026-01-01", "2026-10-01"
seen = set()
with open(f"{handle}.jsonl", "w") as f:
for start, end in months(since, until):
for t in window(handle, start, end):
if t["id"] not in seen and since <= (t["createdAt"] or "") < until:
seen.add(t["id"])
f.write(json.dumps(t) + "\n")
print(f"saved {len(seen)} tweets")
If you only need recent weeks, swap window() for timeline(). It's simpler and stops on its own once tweets are older than since. A few details keep it reliable:
- Dedupe on
id. Monthly windows can overlap at the edges, and timeline pages can overlap too if the account tweets while you're paging. - Filter by
createdAtin code, so the range is exact no matter how the operator handles boundaries. - Treat
max_pagesas a budget. Every page is a charge, and a busy account can run to hundreds of pages. - Store the raw JSON. Likes and views change over time, so a snapshot dated to when you pulled it is more honest than a single "current" number.
What it costs
At 3 credits for about 20 tweets per page, plan on roughly 15 credits per 100 tweets. An account posting 10 times a day produces about 300 tweets a month, which is 15 pages and 45 credits. A full 3,200-tweet timeline comes to around 160 pages, or about 480 credits. Failed calls cost nothing. Every response includes credits.remaining, and the script prints it so you can stop early.
Search vs timeline: which to use
| You want | Use | Why |
|---|---|---|
| Tweets matching words in a date range | x.search with since/until | Operators filter on X's side, so you only pay for matches |
| An account's last few weeks | x.tweets | Newest first, no query to tune |
| One account across older months | x.search with from: per month | Each window is independent of how much the account posted since |
| Your own complete history | X's archive download | It's the only complete record, and it's free |
| A live feed of new matches | x.search with sort=latest on a schedule | Dedupe on id between runs |
If you also track Reddit, our AI agent guide to Reddit and X data shows how to wire these endpoints into an agent. To have an agent run the searches for you in plain English, the best MCP servers for Reddit and X compares the options. To search Reddit and X together in one request, see how to search Reddit and X in one API call.
The case for just using X's own tools
You can make a fair case for skipping APIs entirely. For your own tweets, the archive download is complete, free and yours. Searching it locally with a script, or handing it to an AI tool as several people above did, beats any search endpoint because there's no search index in the way. For occasional lookups of other accounts, the operators in X search cost nothing.
An API is worth it when you need other people's public tweets as data, on a schedule, or across many accounts and date ranges. It won't fix the gaps in X search, since a search endpoint can only return what X search returns. The timeline endpoint and date windows reduce that dependence. If completeness is a legal or research requirement, though, check X's terms and consider X's own paid API, which offers full-archive search on pay-per-use plans according to X's search docs.
Try it
ChatterAPI gives your code and your agents Reddit and X data through one key, over REST or MCP, and you don't have to apply for a developer account. Sign up with Google and you get 100 free credits without entering a card. That covers about 33 pages of tweet search, or roughly 650 archived tweets. Try the script above on an account you follow. Plans start at $19 a month for 25,000 credits.