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.
- Cloudflare's bot protection returns
403to a barePython-urlliborlibwww-perlUser-Agent. - Use
requests,curlor another standard HTTP client 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: URLs posted by @malwrhunterteam in the last month
https://api.tweetfeed.live/v1/month/@malwrhunterteam/url
Add ?defang=1 to defang IOC values: hxxps://evil[.]tld, 1[.]2[.]3[.]4.
- GitHub-flavoured markdown does not auto-link
hxxps://evil[.]tldorevil[.]tld(tested 2026-09-05). - Other chat and ticket tools vary.
- Absent, empty,
0orfalsereturns the same response as withoutdefang. - Any other value returns
400. - On
/v1/today,/v1/weekand/v1/month(with or without filters) only each row'svalueis defanged. /v1/yearredirects to a raw CSV and is never defanged.- Full field list for
/v1/ioc: see IOC lookup.
curl 'https://api.tweetfeed.live/v1/today/phishing/url?defang=1'
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 can exceed it, and only in high-volume months: when it does, the oldest days of the window are dropped and the response carries X-Result-Truncated: true. 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-Matchis the recommended validator:ETagchanges only when the response body actually changes.Last-Modifiedreflects when this Worker cached the feed data behind the response, not when that data last changed.- So
If-Modified-Sinceonly saves a round trip while that cache entry is still alive. - If a request sends both,
If-None-Matchwins (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
200with an empty array. - Timestamp older than 365 days returns
410 Gone. - Malformed ISO returns
400. - Strict ISO 8601 with
Zor+HH:MMoffset 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 that is safe to page.
- While
X-Result-Truncatedis present, passX-Result-Window-Endback as the nextsince. - Repeat until the header stops appearing.
Reach is the trailing 30 days, not 365.
- The route is served from the 30-day window file, because scanning the full year exceeds the Worker's CPU budget.
- A
sinceolder than that still returns200, but the payload starts at the oldest row available, not at your timestamp. - The response says so via
X-Result-Window-Incomplete: truealongside the realX-Result-Window-Start. - For a deeper backfill pull year.csv once, then switch to this endpoint for incremental updates.
Campaigns (AI-clustered)
Groups related IOCs from the last 30 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": "month",
"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,
"targeted_sector": null,
"targeted_country": null,
"ttps": ["T1566.002", "T1583.006"],
"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: truemeans the daily clustering run failed and this document is the previous day's snapshot.stale_sinceholds the date of the last successful run.- Human page: tweetfeed.live/campaigns/.
Four newer optional keys, each omitted when empty:
patterns(optional, max 3): regexes generalised from the campaign's own registered domains.- Deterministic. Each has
domain_count,ioc_count, up to 3examplesandfirst_seen/last_seen. domains_elsewhere_30d: unowned 30-day feed domains the regex also matches.- Omitted when no pattern passes the gates.
- Currently shadow upstream, so absent on most documents.
- Deterministic. Each has
history(optional): 365-day evidence behind the 30-day card, from a scan of the publicyear.csv.- Fields:
window_days,first_seen_365d/last_seen_365d,domains_365d,iocs_365d,iocs_before_window. by_pattern:{"regex", "domains_365d", "first_seen_365d"}per naming pattern.- Omitted when the yearly scan failed or timed out.
- Fields:
related(optional): phishunt.io detections active at build time on the campaign's domains (exact) or naming patterns (pattern).- Fields:
source,checked_at,match_countand up to 10matchesof{"domain", "match", "company", "asn"}. - Never members, never in
ioc_count. - Omitted when nothing matches or the fetch failed.
- Fields:
anchors.families(optional): only on an orphan hash/IP bucket that local enrichment attributed to one malware family.- Such a bucket has no domain/path/tag anchor.
- The shared family is what makes it one campaign.
The document above lists each campaign's metadata plus a 25-IOC sample. Full IOC membership - every IOC in every campaign, verbatim from the feed - is served separately by this endpoint, so the hot /v1/campaigns document stays small. Same campaign ids, regenerated daily alongside it.
GET https://api.tweetfeed.live/v1/campaigns/iocsResponse (trimmed)
{
"version": 1,
"generated_at": "2026-09-04T06:56:16Z",
"campaigns": {
"tfc-0eebc91ceb12": [
{
"date": "2026-08-25 13:42:00",
"tags": ["#stealer"],
"tweet": "https://x.com/suyog41/status/2092246114179432584",
"type": "domain",
"user": "suyog41",
"value": "lewokodwqko.icu"
},
{
"date": "2026-08-25 13:42:00",
"tags": ["#stealer"],
"tweet": "https://x.com/suyog41/status/2092246114179432584",
"type": "url",
"user": "suyog41",
"value": "http://lewokodwqko.icu"
},
"..."
]
}
}
Per-campaign export
Need the full record for one campaign - header plus every member IOC, no 25-row sample cap - without the ~231 KB shared /v1/campaigns/iocs document? Fetch it directly by campaign id, in JSON, CSV, or a ready-to-import STIX 2.1 bundle.
GET https://api.tweetfeed.live/v1/campaigns/{id}
Examples
curl -s https://api.tweetfeed.live/v1/campaigns/tfc-6d1f6fb9260c
curl -s https://api.tweetfeed.live/v1/campaigns/tfc-6d1f6fb9260c.csv
curl -s https://api.tweetfeed.live/v1/campaigns/tfc-6d1f6fb9260c.stix.json
{id}matches^tfc-[0-9a-f]{12}$- get valid ids from/v1/campaigns.- The
.csvmirrors the same 6 columns astoday.csv. - The
.stix.jsonis a self-contained STIX 2.1 bundle (Identity, TLP:CLEAR marking, one Indicator per IOC). - Unknown or expired id returns
404with a JSON{"error": "..."}body. - Same ETag/Last-Modified conditional-request support as every route above.
- Building an agent integration? The MCP server exposes the same data as a
get_campaign_iocstool.
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",
"hours": [12, 8, 5, 0, 3, 9, 14, 11, 10, 18, 16, 13, 15, 12, 19, 17, 11, 10, 11, 0, 0, 0, 0, 0]
},
"week": {
"total": 1490,
"types": { "...": "..." },
"tags": { "...": "..." },
"first_date": "2026-07-23 00:01:09",
"last_date": "2026-07-29 18:11:47",
"days": [
{ "date": "2026-07-23", "count": 201 },
{ "date": "2026-07-24", "count": 233 },
"..."
]
},
"month": {
"total": 6320,
"types": { "...": "..." },
"tags": { "...": "..." },
"first_date": "2026-06-30 00:00:41",
"last_date": "2026-07-29 18:11:47",
"days": [
{ "date": "2026-06-30", "count": 187 },
{ "date": "2026-07-01", "count": 240 },
"..."
]
},
"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]
}
},
"tag_names": { "cobaltstrike": "#CobaltStrike", "phishing": "#phishing", "...": "..." }
}
hours(only present ontoday) has 24 integers, indexed by UTC hour: index0is 00:00-00:59 UTC.days(only present onweekandmonth) lists{"date", "count"}objects, ascending by UTC date. Days with no IOCs are left out.tagskeys are lowercase without#.tag_namesmaps each key to its most common spelling in the posts, with#.months(only present onyear) is indexed by calendar month, not relative to the window.- Index
0is always January, index11is always December. - Any window with no IOCs reports
first_date/last_dateasnull.
Status
Freshness facts for every published artifact plus a per-request verdict: is the pipeline stale, how old is each file, and (for the raw feed files) how old is the newest row inside it. Computed fresh on every request, never served from the response cache - poll this instead of re-cloning the data repo to find out if anything moved. The /feeds/ headline chip reads this endpoint.
GET https://api.tweetfeed.live/v1/statusResponse (trimmed)
{
"schema_version": 1,
"generated_at": "2026-09-05T14:45:56Z",
"pipeline": { "interval_seconds": 900, "stale_after_seconds": 1800 },
"artifacts": {
"today.csv": {
"kind": "tick",
"updated_at": "2026-09-05T14:45:56Z",
"rows": 28,
"last_row": "2026-09-05 14:32:24",
"data_stale_after_seconds": 28800,
"age_seconds": 122,
"stale": false,
"data_age_seconds": 934,
"data_stale": false
},
"archive": {
"kind": "change-driven",
"updated_at": "2026-09-05T14:45:54Z",
"stale_after_seconds": null,
"note": "generated_at moves only when the archive corpus changes (at most daily); pipeline freshness is the signal",
"age_seconds": 124,
"stale": false
},
"campaigns.json": {
"kind": "own-clock",
"updated_at": "2026-09-05T06:43:57Z",
"interval_seconds": 86400,
"stale_after_seconds": 93600,
"age_seconds": 29041,
"stale": false
},
"...": "one entry per published artifact - today/week/month/year.csv, rss.xml, counts.json, trends.json, contributors.json, campaigns.json, external/ipmeta/enrichment/domainmeta.json, exclusive.csv, misp/, stix/, blocklists/, lookup/"
},
"checked_at": "2026-09-05T14:47:58Z",
"age_seconds": 122,
"stale": false,
"any_stale": false,
"sources": {
"total": 47, "hashtag": 38, "account": 9, "rows_last_24h": 105,
"dead_after_seconds": { "account": 2592000, "hashtag": null },
"with_rows_last_24h": 31, "dead": 3, "dead_ids": ["500mk500", "byrne_emmy12099", "Fact_Finder03"], "any_dead": true,
"items": [
{ "id": "phishing", "kind": "hashtag", "newest_item_at": "2026-09-10T20:05:12Z", "rows_last_24h": 30, "dead_after_seconds": null, "age_seconds": 240, "dead": null },
{ "id": "Fact_Finder03", "kind": "account", "newest_item_at": "2026-08-10T14:09:12Z", "rows_last_24h": 0, "dead_after_seconds": 2592000, "age_seconds": 2700000, "dead": true }
]
}
}
- Top-level
stale: truemeans the pipeline document itself is older thanstale_after_seconds. - It is the same signal the /feeds/ freshness chip renders.
- Per-artifact
staleis scoped to that one file. any_staleis true if any single artifact is stale, even while the top-level pipeline is fresh.kind: "change-driven"artifacts (archive,domainmeta.json) carrystale_after_seconds: null.- Their
generated_atonly moves when the underlying content changes, so there is no fixed staleness window. - Watch the pipeline-level fields instead.
kind: "tick"artifacts share the pipeline's own clock and additionally expose data freshness vialast_rowanddata_stale.- The newest row inside a file can be older than the file itself, e.g. a quiet tag.
sourcestracks per-X-source coverage: oneitemsentry per enabled hashtag/account feed.- Each entry has
newest_item_atandrows_last_24hattributed to it. - A row tagged with two sources counts toward both; the block-level
rows_last_24hcounts distinct rows. - Only
accountsources get adeadverdict (age pastdead_after_seconds, 30 days). - Hashtags carry
dead_after_seconds: nullanddead: nullon purpose, since a quiet tag for weeks is normal. age_seconds/dead/dead_ids/any_dead/with_rows_last_24hare computed per request.- Same as the top-level
stale. sourcesdoes not affectstale/any_staleor theX-Feed-*headers.
- Never conditional: this document is computed fresh on every request.
- Its
ETagchanges every time, soIf-None-Matchwill never return a304here. - Cache it with
cache-control: public, max-age=60instead. - Headers
X-Feed-Stale("true"/"false") andX-Feed-Age-Secondsmirror the top-level verdict without parsing the body. - The underlying
status.jsonis re-read from the data repo at most every 5 minutes, with no stale-while-revalidate. - Once that window expires the request blocks on a fresh read, falling back to the last cached copy only if that read fails.
Manifest
Every published artifact in one document: byte size, row count, SHA-256, last-updated timestamp, plus both the raw GitHub URL and (where one exists) the API URL. Diff the sha256 against your last pull instead of re-downloading files that haven't changed.
GET https://api.tweetfeed.live/v1/manifestResponse (trimmed)
{
"schema_version": 1,
"generated_at": "2026-09-05T14:45:56Z",
"base_url": "https://raw.githubusercontent.com/0xDanielLopez/TweetFeed/master/",
"api_base": "https://api.tweetfeed.live",
"artifacts": [
{
"name": "today.csv",
"format": "csv",
"window": "today",
"bytes": 3742,
"rows": 28,
"sha256": "deba6834e29dcaaa889b7a30952f1ddd14aeb184e6ffdb6284285ab265f588c5",
"updated_at": "2026-09-05T14:45:56Z",
"url": "https://raw.githubusercontent.com/0xDanielLopez/TweetFeed/master/today.csv",
"api_url": "https://api.tweetfeed.live/v1/today"
},
"..."
],
"families": [
{ "name": "lookup", "path": "lookup/", "shards": 256, "meta": "lookup/meta.json" },
{ "name": "misp", "path": "misp/", "manifest": "misp/manifest.json" },
"..."
]
}
familiescovers sharded/multi-file surfaces that don't reduce to one row.- Covered:
lookup/,misp/,stix/,archive/,campaigns/, per-tag/type/user RSS. - See their own sections above for shard layout.
sha256/bytes/rowsdescribe the files in the same data-repo commit as this manifest.- The API resolves
masterfresh on every fetch. - Across a tick boundary, a locally computed hash may not match what
/v1/*serves at that instant.
Supports ETag/304 like /v1/counts: send If-None-Match back and get an empty 304 when nothing changed.
Trends
Aggregate analytics computed from the same 15-minute pipeline that feeds the raw IOC endpoints: a rolling 31-day daily IOC volume series with a per-type breakdown, week-over-week top-moving tags, the most-abused TLDs among malicious domains from the last 30 days, and the novelty rate (new vs. recurring share of this week's distinct IOC values). Human page: tweetfeed.live/trends/. Building an agent integration? The MCP server exposes the same data as a get_trends tool.
GET https://api.tweetfeed.live/v1/trendsResponse (trimmed)
{
"version": 1,
"generated_at": "2026-08-04T19:00:36Z",
"daily": {
"days": 31,
"dates": ["2026-07-05", "2026-07-06"],
"total": [62, 154],
"types": { "url": [30, 71], "domain": [22, 60], "ip": [7, 15], "sha256": [2, 5], "md5": [1, 3] }
},
"movers": {
"current_range": ["2026-07-29", "2026-08-04"],
"previous_range": ["2026-07-22", "2026-07-28"],
"tags": [{ "tag": "phishing", "count": 783, "previous": 3600, "delta": -2817, "pct": -78.2 }]
},
"tlds": { "window": "month", "domains_total": 5991, "top": [{ "tld": "com", "count": 1601 }], "other": 1931 },
"novelty": { "window": "week", "distinct_values": 1579, "new": 1504, "recurring": 75, "pct_new": 95.3 },
"producers": { }
}
dailyis column-oriented:dates,totaland eachtypesentry are parallel arrays sharing one index.- The most recent day is partial until 00:00 UTC.
moverscompares the last 7 days against the 7 before them, sopctis negative for a tag that cooled off.
Contributors
Per-window reporter leaderboard: today/week/month/year rankings of X/Twitter handles by IOCs reported, each entry carrying its top 3 IOC types and top 3 tags. Precomputed server-side every 15 minutes so tweetfeed.live/researchers/ renders all four windows from one fetch instead of ranking CSVs in the browser.
GET https://api.tweetfeed.live/v1/contributorsResponse (trimmed)
{
"schema_version": 1,
"generated_at": "2026-09-12T16:30:00Z",
"windows": {
"today": {
"first_date": "2026-09-12 00:01:00",
"last_date": "2026-09-12 16:15:00",
"rows": 31,
"users": 16,
"entries": [
{ "user": "TKemmerling", "iocs": 6, "top_types": ["url", "domain"], "top_tags": ["phishing", "scam"], "first_seen": "2026-09-12 07:10:00", "last_seen": "2026-09-12 15:45:00" }
]
},
"week": "...", "month": "...", "year": "..."
}
}
entriesare pre-ranked byiocsdescending, tie-broken by handle ascending - no client-side sorting needed.top_types/top_tagsare capped at 3, most frequent first; tags carry no leading#.- An empty window (no IOCs reported in that period) has
rows: 0,users: 0,entries: []and both datesnull. - Handles are merged case-insensitively.
Supports ETag/304 like /v1/counts: send If-None-Match back and get an empty 304 when nothing changed.
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. Also checked against the pre-365-day archive, returned separately in the optional archive block. 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"]
}
],
"reg": {
"apex": "praserfelizlu.site",
"registrar": "NameSilo, LLC",
"created": "2026-07-04T18:02:11Z",
"ns": ["ns1.dns-parking.com", "ns2.dns-parking.com"],
"ips": ["203.0.113.10"],
"asn": "AS13335",
"org": "Cloudflare, Inc.",
"status": "ok",
"checked_at": "2026-07-06T21:30:00Z",
"age_days_at_report": 2,
"newly_registered": true
},
"archive": {
"window": "pre-365d",
"total": 1,
"records": [
{
"type": "url",
"value": "https://clicksafe.nl/",
"first_seen": "2022-01-01 00:09:03",
"last_seen": "2022-01-01 00:09:03",
"count": 1,
"tweet_id": "1477069322681991170",
"tags": ["#phishing"]
}
]
},
"campaigns": [
{
"id": "tfc-6d1f6fb9260c",
"name": "Multi-family RAT/stealer C2 on compromised and DDNS hosts",
"confidence": "high",
"threat_types": ["phishing"],
"ioc_count": 198,
"last_seen": "2026-09-05 14:30:00"
}
]
}
Unknown values return 200 with found: false. Values containing / or ? must use the ?value= query form.
- The optional
archiveblock covers hits older than the live 365-day window. - It goes back to
first_dateinTweetFeed/archive/meta.json. - It is additive to
found/records, never merged into them. - Their date ranges are disjoint by construction, so never sum
archive.totalwith a record'scount. - It can accompany
found: trueorfound: falsefor the same queried value. - An archive record is a reduced schema versus a live one: no
users,tweetsorrelated.
- The optional
campaignsblock (max 3, newestlast_seenfirst) lists the published campaigns this IOC belongs to. - It is independent of
found: a value can be in a campaign while missing from the live 365-day window. - Each entry links to
/campaigns/#<id>on the campaign explorer. - There you get the full context, targeted brand/sector/country and IOC sample.
- TweetFeed's own feed data and API responses are CC0.
- The optional
externalcorroboration block draws on five third-party sources, each under its own terms, not CC0:- abuse.ch (URLhaus / ThreatFox / MalwareBazaar), see abuse.ch's terms of use.
- USOM data via the elliotwutingfeng/USOM-Blocklists mirror (BSD-3-Clause, originally published by Turkiye's Cyber Security Directorate).
- IPsum by @stamparm (Unlicense).
- The optional
external_exclusiveboolean is set membership only. false:externalhas at least one entry.true: the cross-check resolved and none of the 5 tracked feeds lists the value.- Absent: the cross-check could not be resolved, or the value was not found.
- The optional
net.dropblock is a separate case, IP lookups only. - It annotates that the network hosting this IP has a hit on The Spamhaus Project's DROP/ASN-DROP netblock lists.
- It does not mean the IOC value itself is listed anywhere.
- Shown only as a per-record annotation, governed by Spamhaus's own terms, not CC0.
- The optional
net.rangeblock is also IP lookups only. - It is the official published IP range of a known provider (for example AWS EC2 or GitHub Actions) that contains this IP.
- It describes the network, not the IOC, and it is not a verdict.
regis optional (domain/url lookups): RDAP registration metadata plus two age fields.age_days_at_report: the domain's age in UTC days when TweetFeed first reported it.newly_registered:truewhen that age is 30 days or less.- Both age fields are omitted when the creation date is unknown.
- Add
?defang=1to defang this response. These fields come back ashxxps://evil[.]tld/1[.]2[.]3[.]4:query,records[].value,records[].related[][1],archive.records[].valuereg.apex,reg.ns[]andreg.ips[]
- Never defanged:
ai.summary(prose),external[].linkandnet.*(third-party sidecar fields).
Blocklists
Ready-to-consume exports of the last 30 days, one file per consumer format. Rebuilt every 15 minutes, but a file only changes when its entries change. Community-reported IOCs - use at your own risk.
| Format | Endpoint | Feeds |
|---|---|---|
| Domain list | /v1/blocklist/domains.txt |
Pi-hole, and anything that takes one domain per line |
| hosts file | /v1/blocklist/hosts.txt |
/etc/hosts, Windows hosts file |
| AdGuard rules | /v1/blocklist/adguard.txt |
AdGuard Home, uBlock Origin - valid filter list with a ! metadata header |
| IP list | /v1/blocklist/ips.txt |
Firewalls and edge ACLs |
| DNS RPZ zone | /v1/blocklist/rpz.txt |
BIND, Unbound, PowerDNS Recursor |
| dnsmasq config | /v1/blocklist/dnsmasq.txt |
dnsmasq |
| Full URLs | /v1/blocklist/urls.txt |
Proxies, IDS, SOAR enrichment - consumers that match on path |
| Zeek intel file | /v1/blocklist/zeek-intel.txt |
Zeek Intelligence Framework, via redef Intel::read_files |
| Wazuh domains CDB | /v1/blocklist/wazuh-domains.txt |
Wazuh rule with lookup="match_key" |
| Wazuh IPs CDB | /v1/blocklist/wazuh-ips.txt |
Wazuh rule with lookup="address_match_key" on srcip/dstip |
| Domains (corroborated) | /v1/blocklist/domains-corroborated.txt |
Subset of domains.txt also listed by URLhaus/ThreatFox/MalwareBazaar/USOM/IPsum |
| IPs (corroborated) | /v1/blocklist/ips-corroborated.txt |
Subset of ips.txt also listed by URLhaus/ThreatFox/MalwareBazaar/USOM/IPsum |
| URLs (corroborated) | /v1/blocklist/urls-corroborated.txt |
Subset of urls.txt also listed by URLhaus/ThreatFox/MalwareBazaar/USOM/IPsum |
| SHA-256 hashes | /v1/blocklist/sha256.txt |
Microsoft Defender for Endpoint Indicators import, CrowdStrike IOC management, EDR/AV bulk import |
| MD5 hashes | /v1/blocklist/md5.txt |
Same use case as sha256.txt for tools keyed on MD5 |
| Newly registered domains | /v1/blocklist/nrd-domains.txt |
Subset of domains.txt with RDAP apex age <=30 days at first report - DNS filters wanting a high-signal NRD+reported list |
- The DNS/host formats block at that level.
urls.txtis one full URL per line instead.- So it also covers
url-type IOCs on shared or otherwise legitimate infrastructure that DNS-level blocking cannot safely reach. zeek-intel.txt,wazuh-domains.txtandwazuh-ips.txtcarry no#comment header at all:zeek-intel.txtopens with a tab-separated#fieldsline (indicator/indicator_type/meta.source, the last alwaystweetfeed.live) instead.- The two Wazuh files have no header line, since a
#would compile as a literal CDB key. - Check freshness for these three with /v1/manifest or the response's
Last-Modifiedheader.
adguard.txtcarries an AdGuard/uBlock filter-list metadata header rather than the generic#one.- Header lines:
! Title:,! Homepage:,! License:,! Expires:, etc. - That makes it a valid filter list for AdGuard Home / uBlock Origin and filter-list registries.
- Header lines:
nrd-domains.txtadds extra header lines naming its base list and the RDAP cross-check's last refresh.- Palo Alto Networks PAN-OS: point a Domain EDL at
domains.txtand an IP EDL atips.txt. - Do not use
urls.txtas a URL EDL, since PAN-OS reads? & = ; +as token separators.
# 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
limitandnext. - Media type
application/taxii+json;version=2.1. - An
added_afterpoll returns indicators only. - Every indicator references an
identity(created_by_ref) and amarking-definition(object_marking_refs). - Both were added when the collection was created, so they fall outside any recent window.
- Fetch them once by id from
/objects/{id}/(or withmatch[id]). - Then 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.
How do I use TweetFeed IOCs in my SIEM or threat intelligence platform?
See Hunt: copy-paste recipes for the terminal, Splunk, Microsoft Sentinel and Defender, Elastic, IBM QRadar, MISP and TAXII, OpenCTI, IntelOwl, Sigma detection content and AI agents. Every recipe uses the same free, unauthenticated, CC0 API described on this page.