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 403 to requests sent with a bare Python-urllib or libwww-perl User-Agent - use requests, curl or 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/today 
Example 2: URLs posted today with phishing tag
https://api.tweetfeed.live/v1/today/phishing/url 
Example 3: IPs posted in the last 7 days with CobaltStrike tag
https://api.tweetfeed.live/v1/week/cobaltstrike/ip 
Example 4: URLs posted by  @malwrhunterteam  in the last month
https://api.tweetfeed.live/v1/month/@malwrhunterteam/url 

Add ?defang=1 to defang IOC values in the response: hxxps://evil[.]tld, 1[.]2[.]3[.]4. GitHub-flavoured markdown does not auto-link hxxps://evil[.]tld or evil[.]tld (tested 2026-09-05); other chat and ticket tools vary. Absent, empty, 0 or false returns the same response as without defang; any other value returns 400. On /v1/today, /v1/week and /v1/month (with or without filters) only each row's value is defanged - /v1/year redirects to a raw CSV and is never defanged; see IOC lookup for the full field list on /v1/ioc.

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 as false.

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/*).

Example: revalidate with If-None-Match
curl -H 'If-None-Match: "a1b2c3d4e5f60718"' -i https://api.tweetfeed.live/v1/today 
Example: 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. 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.

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 since older than that still returns 200, but the payload starts at the oldest row available rather than at your timestamp, and the response says so via X-Result-Window-Incomplete: true alongside the real X-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/campaigns 
Response (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: 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/.

Four newer optional keys, each omitted when empty. patterns (optional, max 3): deterministic regexes generalised from the campaign's own registered domains, each with domain_count, ioc_count, domains_elsewhere_30d (unowned 30-day feed domains the regex also matches), up to 3 examples and first_seen/last_seen; omitted when no pattern passes the gates, and currently shadow upstream so absent on most documents. history (optional): 365-day evidence behind the 30-day card, from a scan of the public year.csv - window_days, first_seen_365d/last_seen_365d, domains_365d, iocs_365d, iocs_before_window and by_pattern ({"regex", "domains_365d", "first_seen_365d"} per naming pattern); omitted when the yearly scan failed or timed out. related (optional): phishunt.io detections active at build time on the campaign's domains (exact) or naming patterns (pattern) - source, checked_at, match_count and up to 10 matches of {"domain", "match", "company", "asn"}; never members, never in ioc_count, omitted when nothing matches or the fetch failed. 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/iocs 
Response (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 .csv mirrors the same 6 columns as today.csv; the .stix.json is a self-contained STIX 2.1 bundle (Identity, TLP:CLEAR marking, one Indicator per IOC). Unknown or expired id returns 404 with 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_iocs tool.


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/counts 
Response (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.


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/status 
Response (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, 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
}

Top-level stale: true means the pipeline document itself is older than stale_after_seconds - the same signal the /feeds/ freshness chip renders. Per-artifact stale is scoped to that one file; any_stale is true if any single artifact is stale even while the top-level pipeline is fresh. kind: "change-driven" artifacts (archive, domainmeta.json) carry stale_after_seconds: null - their generated_at only moves when the underlying content changes, so there is no fixed staleness window for them; watch the pipeline-level fields instead. kind: "tick" artifacts share the pipeline's own clock and additionally expose data freshness via last_row and data_stale - the newest row inside a file can be older than the file itself, e.g. a quiet tag.

Never conditional: this document is computed fresh on every request, so its ETag changes every time and If-None-Match will never return a 304 here. Cache it with cache-control: public, max-age=60 instead. Headers X-Feed-Stale ("true"/"false") and X-Feed-Age-Seconds mirror the top-level verdict without parsing the body. The underlying status.json is 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/manifest 
Response (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" },
        "..."
    ]
}

families covers sharded/multi-file surfaces (lookup/, misp/, stix/, archive/, campaigns/, per-tag/type/user RSS) that don't reduce to one row - see their own sections above for shard layout. sha256/bytes/rows describe the files in the same data-repo commit as this manifest: the API resolves master fresh on every fetch, so across a tick boundary the hash you compute locally may not match what /v1/* happens to be serving at that instant.

Supports ETag/304 like /v1/counts: send If-None-Match back and get an empty 304 when nothing changed.


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/trends 
Response (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": { }
}

daily is column-oriented: dates, total and each types entry are parallel arrays sharing one index. The most recent day is partial until 00:00 UTC. movers compares the last 7 days against the 7 before them, so pct is negative for a tag that cooled off.


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 archive block covers hits older than the live 365-day window, back to first_date in TweetFeed/archive/meta.json. It is additive to found/records, never merged into them - their date ranges are disjoint by construction, so never sum archive.total with a record's count. It can accompany found: true or found: false for the same queried value. An archive record is a reduced schema versus a live one: no users, tweets or related.

The optional campaigns block (max 3, newest last_seen first) lists the published campaigns this IOC belongs to, 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 for the full context, targeted brand/sector/country and IOC sample.

TweetFeed's own feed data and API responses are CC0; the optional external corroboration 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), and IPsum by @stamparm (Unlicense).

The optional external_exclusive boolean is set membership only: false when external has at least one entry, true when the cross-check resolved and none of the 5 tracked feeds lists the value, and absent when the cross-check could not be resolved or the value was not found.

The optional net.drop block 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.

reg is optional (domain/url lookups): RDAP registration metadata plus age_days_at_report, the domain's age in UTC days when TweetFeed first reported it, and newly_registered (true when that age is 30 days or less). Both age fields are omitted when the creation date is unknown.

Add ?defang=1 to defang this response: query, records[].value, records[].related[][1], archive.records[].value, reg.apex, reg.ns[] and reg.ips[] come back as hxxps://evil[.]tld / 1[.]2[.]3[.]4; ai.summary, external[].link and net.* are never defanged (the first is prose, the other two are 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.txt is 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.txt and wazuh-ips.txt carry no # comment header at all - zeek-intel.txt opens with a tab-separated #fields line (indicator/indicator_type/meta.source, the last always tweetfeed.live) instead, and the two Wazuh files have no header line, since a # would compile as a literal CDB key - so check freshness for these three with /v1/manifest or the response's Last-Modified header. adguard.txt instead carries an AdGuard/uBlock filter-list metadata header (! Title:, ! Homepage:, ! License:, ! Expires:, etc.) rather than the generic # one, making it a valid filter list for AdGuard Home / uBlock Origin and filter-list registries. nrd-domains.txt adds extra header lines naming its base list and the RDAP cross-check's last refresh. For Palo Alto Networks PAN-OS, point a Domain EDL at domains.txt and an IP EDL at ips.txt; do not use urls.txt as a URL EDL, since PAN-OS reads ? & = ; + as token separators.

Response (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.

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.