TweetFeed API
Free REST API for Threat Intelligence queries (limited to 10k results per search)
TweetFeed API
TweetFeed.live API (limited to 10k results per search)
Introduction
Free API to get IOCs shared by the community on Twitter/X. Building an AI agent? Use the MCP server instead.
Endpoint
GET https://api.tweetfeed.live/v1/{time}/{filter1}/{filter2}Example 1: IOCs posted today
https://api.tweetfeed.live/v1/todayExample 2: URLs posted today with phishing tag
https://api.tweetfeed.live/v1/today/phishing/urlExample 3: IPs posted in the last 7 days with CobaltStrike tag
https://api.tweetfeed.live/v1/week/cobaltstrike/ipExample 4: SHA256 hashes posted by @malwrhunterteam in the last month
https://api.tweetfeed.live/v1/month/@malwrhunterteam/sha256
Result cap
A response is capped at 10,000 rows, truncated from the oldest end so you keep the most recent data. In practice only the unfiltered /v1/month exceeds it - that window holds about 15,000 rows, so roughly its first ten days are dropped. today, week and every filtered variant stay below the cap.
Truncation is never silent. Every 200 reports what you actually received:
X-Result-Count- rows in this response.X-Result-Window-Start/X-Result-Window-End- the real date range of the payload.X-Result-Truncated: true- sent only when the cap was hit. Test for presence; it is never sent asfalse.
Need the complete 30-day window? The CSV feed is served in full, with no cap:
https://tweetfeed.live/feeds/month.csv
Caching and conditional requests
Every 200 response carries an ETag and a Last-Modified header. Send either one back on your next request and get a 304 Not Modified with an empty body instead of re-downloading the full payload - this works on every route above, and also on the static apex files (/feeds/*.csv, /rss.xml, /misp/*, /stix/*).
If-None-Match
curl -H 'If-None-Match: "a1b2c3d4e5f60718"' -i https://api.tweetfeed.live/v1/todayExample: revalidate with
curl -z / wget -N
curl -z today.csv -o today.csv https://tweetfeed.live/feeds/today.csv
wget -N https://tweetfeed.live/feeds/today.csv
If-None-Match is the recommended validator: ETag changes only when the response body actually changes. Last-Modified reflects when this Worker cached the feed data behind the response, not when that data last changed, so If-Modified-Since only saves a round trip while that cache entry is still alive. If a request sends both, If-None-Match wins (RFC 7232 section 6).
Diff endpoint (incremental sync)
Returns IOCs added after the given ISO 8601 timestamp. Useful for delta-syncing a blocklist or a Threat Intelligence pipeline without re-pulling the full window. Same /{filter1}/{filter2} syntax as the time-window routes. Use the Z form for unambiguous UTC.
GET https://api.tweetfeed.live/v1/since/{ISO8601}/{filter1}/{filter2}Example A: phishing IOCs added since a given timestamp
https://api.tweetfeed.live/v1/since/2026-05-01T00:00:00Z/phishing
Edge cases: future timestamp returns 200 with an empty array. Timestamp older than 365 days returns 410 Gone. Malformed ISO returns 400. Strict ISO 8601 with Z or +HH:MM offset is required (no ambiguous local-time forms).
The same 10,000-row cap applies, but truncated from the opposite end: this route returns rows oldest-first, so you get a contiguous block starting at your since. That makes it safe to page - when X-Result-Truncated is present, pass X-Result-Window-End back as the next since and repeat until it stops appearing.
Campaigns (AI-clustered)
Groups related IOCs from the last 7 days into named campaign clusters: deterministic pre-grouping (shared registered domain, cross-domain URL path patterns, shared specific tag) plus an AI-generated name and context. The AI only names and describes clusters - it never adds or removes IOCs. Regenerated once a day. Building an agent integration? The MCP server exposes the same data as a get_campaigns tool.
GET https://api.tweetfeed.live/v1/campaignsResponse (trimmed)
{
"version": 1,
"generated_at": "2026-07-05T19:36:35Z",
"window": "week",
"stale": false,
"stale_since": null,
"campaign_count": 23,
"campaigns": [
{
"id": "tfc-6d1f6fb9260c",
"name": "Multi-family RAT/stealer C2 on compromised and DDNS hosts",
"context": "A broad malware distribution campaign delivering AsyncRAT and NjRAT payloads across compromised legitimate websites, newly registered domains, and dynamic DNS hostnames...",
"confidence": "high",
"targeted_brand": null,
"first_seen": "2026-06-29",
"last_seen": "2026-07-05",
"ioc_count": 198,
"types": { "domain": 53, "ip": 32, "md5": 17, "sha256": 15, "url": 81 },
"tags": ["#APT", "#AsyncRAT", "#C2", "#Njrat", "#RAT"],
"reporters": ["0xb1lal", "BlinkzSec"],
"iocs": [
{
"date": "2026-07-05 18:00:00",
"user": "phatomcandle",
"type": "domain",
"value": "vibranovax.click",
"tags": ["#APT", "#malware"],
"tweet": "https://x.com/phatomcandle/status/2073829278639325327"
}
],
"member_cluster_ids": ["tfc-6d1f6fb9260c"],
"anchors": {
"registered_domains": ["vibranovax.click", "..."],
"url_path_patterns": ["/unicorn/aes.js", "..."],
"tags": ["#APT", "#C2"]
}
}
]
}
stale: true means the daily clustering run failed and this document is the previous day's snapshot (stale_since holds the date of the last successful run). Human page: tweetfeed.live/campaigns/.
Counts
Pre-aggregated totals for each time window: overall count, a per-type breakdown, a per-tag breakdown, and the first/last IOC timestamp - without fetching and parsing the full year CSV. Regenerated every 15 minutes alongside the rest of the feed.
GET https://api.tweetfeed.live/v1/countsResponse (trimmed)
{
"generated_at": "2026-07-29T18:15:03Z",
"windows": {
"today": {
"total": 214,
"types": { "url": 120, "domain": 60, "ip": 20, "sha256": 10, "md5": 4 },
"tags": { "phishing": 140, "malware": 50 },
"first_date": "2026-07-29 00:04:11",
"last_date": "2026-07-29 18:11:47"
},
"week": { "total": 1490, "types": { "...": "..." }, "tags": { "...": "..." }, "first_date": "2026-07-23 00:01:09", "last_date": "2026-07-29 18:11:47" },
"month": { "total": 6320, "types": { "...": "..." }, "tags": { "...": "..." }, "first_date": "2026-06-30 00:00:41", "last_date": "2026-07-29 18:11:47" },
"year": {
"total": 118342,
"types": { "...": "..." },
"tags": { "...": "..." },
"first_date": "2025-07-30 03:12:01",
"last_date": "2026-07-29 18:11:47",
"months": [9812, 8765, 10230, 9540, 11020, 12480, 10990, 9800, 8700, 9100, 9400, 8500]
}
}
}
months (only present on year) is indexed by calendar month, not relative to the window - index 0 is always January, index 11 is always December. Any window with no IOCs reports first_date/last_date as null.
IOC lookup (exact match)
Checks whether a specific IOC appears in the trailing 365 days and returns one aggregated record per IOC type: first/last seen, times seen, reporters, tags and the latest source tweets. URL-encode the value. Defanged input (evil[.]tld, hxxps://) and http/https variants resolve to the same record. Building an agent integration? The MCP server exposes this endpoint as the enrich_ioc tool.
GET https://api.tweetfeed.live/v1/ioc?value={ioc}
Response
{
"found": true,
"query": "praserfelizlu.site",
"window": "365d",
"records": [
{
"type": "domain",
"value": "praserfelizlu.site",
"first_seen": "2026-07-06 21:19:00",
"last_seen": "2026-07-06 21:19:00",
"count": 1,
"users": ["Coolcarlos17"],
"tags": ["#phishing", "#scam"],
"tweets": ["https://x.com/Coolcarlos17/status/2074241960101941737"]
}
]
}
Unknown values return 200 with found: false. Values containing / or ? must use the ?value= query form.
Blocklists
Ready-to-consume exports of the last 30 days for Pi-hole, AdGuard Home, hosts files, DNS resolvers (BIND, Unbound, PowerDNS Recursor) and dnsmasq, plus firewalls. Rebuilt every 15 minutes, but a file only changes when its entries change. Community-reported IOCs - use at your own risk.
GET https://api.tweetfeed.live/v1/blocklist/domains.txt
GET https://api.tweetfeed.live/v1/blocklist/hosts.txt
GET https://api.tweetfeed.live/v1/blocklist/adguard.txt
GET https://api.tweetfeed.live/v1/blocklist/ips.txt
GET https://api.tweetfeed.live/v1/blocklist/rpz.txt
GET https://api.tweetfeed.live/v1/blocklist/dnsmasq.txtResponse (header)
# TweetFeed blocklist - plain domain list (Pi-hole compatible) # Window: 30 days # Entries: 4399 # Updated: 2026-07-12T21:11:25Z # License: CC0 # Community-reported IOCs. Use at your own risk. 008963466.weebly.com 06updatelive.xyz ...
TAXII 2.1
Read-only TAXII 2.1 server exposing the same STIX 2.1 data (indicators, identity, TLP:CLEAR marking) for Threat Intelligence Platforms such as OpenCTI or ThreatQ. No authentication.
GET https://api.tweetfeed.live/taxii2/Example: incremental poll of the rolling 31-day collection
GET https://api.tweetfeed.live/taxii2/root/collections/b7dc78af-1d12-5059-898c-3f0e77636204/objects/?added_after=2026-07-01T00:00:00Z
Paginate with limit and next. Media type application/taxii+json;version=2.1. An added_after poll returns indicators only: the identity and marking-definition objects that every indicator references via created_by_ref and object_marking_refs were added when the collection was created, so they fall outside any recent window. Fetch them once by id from /objects/{id}/ (or with match[id]) so your platform can resolve the attribution and the TLP:CLEAR marking.
Details
| Key | Required? | Possible values |
|---|---|---|
| time | Required |
Select 1 of these timeframes.
today (Today starting 00:00 UTC) week (Last 7 days) month (Last 30 days) year (Last 365 days, full CSV file - filters do not apply) |
| filter1 | Optional |
Can be an specific user, type or tag.
Type (url / domain / ip / sha256 / md5) Tag (phishing / ransomware / CobaltStrike ...) User (@malwrhunterteam / @1ZRR4H / @MBThreatIntel ...) |
| filter2 | Optional |
Can be an specific user, type or tag.
Type (url / domain / ip / sha256 / md5) Tag (phishing / ransomware / CobaltStrike ...) User (@malwrhunterteam / @1ZRR4H / @MBThreatIntel ...) |
Output example
[
{
"date": "2022-05-11 19:39:06",
"user": "Unit42_Intel",
"type": "ip",
"value": "138.124.183.147",
"tags": [
"#CobaltStrike",
"#IcedID"
],
"tweet": "https://x.com/Unit42_Intel/status/1524474195471745028"
},
{
"date": "2022-05-11 19:43:48",
"user": "GootLoaderSites",
"type": "url",
"value": "https://www.kipperfamily.co.uk/forum.php",
"tags": [
"#GootLoader"
],
"tweet": "https://x.com/GootLoaderSites/status/1524475379997675522"
},
{
"date": "2022-05-11 20:00:44",
"user": "ecarlesi",
"type": "url",
"value": "https://tinpan.top/bancocuscatlan/",
"tags": [
"#phishing"
],
"tweet": "https://x.com/ecarlesi/status/1524479640584466432"
},
...
]
Frequently asked questions
Does the TweetFeed API require authentication?
No. The TweetFeed REST API is open and does not require an API key, OAuth token or registration.
How do I query IOCs by type?
Call GET https://api.tweetfeed.live/v1/{time}/{type} where time is today, week or month and type is url, domain, ip, sha256 or md5. The response is JSON by default.
Can I filter by tag or malware family?
Yes. Chain a second filter: GET /v1/{time}/{filter1}/{filter2}. Filters can be an IOC type, a generic tag (phishing, malware) or a malware family (LockBit, RansomHub, and others).
Does the year timeframe support type, tag or user filters?
No. GET /v1/year (and any path under it) returns an HTTP 302 redirect to the full year.csv file - the last 365 days of IOCs, unfiltered. Filter it client-side after downloading, or use today, week or month for JSON responses with type, tag or user filtering.
Is there a rate limit on the TweetFeed API?
No hard rate limit for normal use. The API runs on Cloudflare Workers with edge caching; the tweetfeed.live zone serves roughly 45,000 requests per day. This is a side project, not an SLA-backed service, so be reasonable.
How do I avoid re-downloading the whole feed?
Use conditional requests: every 200 response carries an ETag and a Last-Modified header, send either one back and get a 304 with an empty body when nothing changed. See Caching and conditional requests above.