IOC feeds
Export the indicators your workspace has confirmed — as JSON for SOAR pipelines, STIX 2.1 for threat-intel platforms, or plain-text EDL lists your firewall can consume directly.
All feed endpoints require a key with the IocExport scope.
JSON / STIX
# JSON (default)
curl "https://api.urlyze.io/api/feed/iocs" -H "X-Api-Key: YOUR_API_KEY"
# STIX 2.1 bundle
curl "https://api.urlyze.io/api/feed/iocs?format=stix" -H "X-Api-Key: YOUR_API_KEY"
EDL text lists
One indicator per line, ready to be polled as an External Dynamic List by Palo Alto, Fortinet and friends:
curl "https://api.urlyze.io/api/feed/edl/domains" -H "X-Api-Key: YOUR_API_KEY"
curl "https://api.urlyze.io/api/feed/edl/urls" -H "X-Api-Key: YOUR_API_KEY"
curl "https://api.urlyze.io/api/feed/edl/ips" -H "X-Api-Key: YOUR_API_KEY"
X-Api-Key header configured
and a refresh interval of 5–15 minutes. Full parameters (age window, severity
filter, pagination) are in the API Reference.
Indicator types
Every row carries an indicatorType. This is the full list; unknown values may
appear as the list grows, so map what you know and keep the rest as-is.
indicatorType |
what it is | EDL list | STIX pattern |
|---|---|---|---|
domain |
the scanned page's host | domains |
domain-name:value |
url |
the scanned page's URL | urls |
url:value |
ip |
the scanned page's IP | ips |
ipv4-addr:value / ipv6-addr:value |
url:stager |
an http(s) URL written in the ClickFix command the page put on the clipboard (the stager or C2 the victim would fetch) | urls |
url:value |
ip:stager |
the IP when a url:stager host is an IP literal |
ips |
ipv4-addr:value / ipv6-addr:value |
command |
the clipboard command itself, verbatim | never | process:command_line |
url:delivery |
the executable the page delivers (RMM installer, dropper), query string included — for an abused RMM tenant it carries the relay and session ids | urls |
url:value |
ip:delivery |
the IP when a url:delivery host is an IP literal |
ips |
ipv4-addr:value / ipv6-addr:value |
sha256:file |
SHA-256 of a delivered file our analyser convicted (never a Clean file) | never | file:hashes.'SHA-256' |
url:next-hop |
an http(s) URL written in the text of a delivered script (the next stage it names) | urls |
url:value |
contract:evm |
CAIP-10 eip155:<chainId>:<address> (address lower-case) — a
smart contract the page read through a public blockchain RPC node, published only when the
scan concluded blockchain-hosted C2 (EtherHiding): the page contacted a node
while carrying no crypto context. A legitimate dApp reads contracts too and never produces
this row. See below. |
never | x-urlyze-evm-contract:value |
rmm:operator |
the remote-access account a delivered installer connects to:
screenconnect:instance:<tenant>.screenconnect.com (vendor-hosted),
screenconnect:relay:<host>[:<port>] (self-hosted),
fleetdeck:agent:<token>. See below. |
never | x-urlyze-rmm-operator:value |
exfil:operator |
the non-secret id of the account harvested data is sent to:
telegram:bot:<botId>, discord:webhook:<webhookId>. See
below. |
never | x-urlyze-exfil-operator:value |
sha256:script |
SHA-256 of a script the page loaded | never | file:hashes.'SHA-256' |
sha256:page sha256:dom phash:favicon
phash:screenshot jarm ja4x
exfilEndpoint |
Urlyze structural and perceptual fingerprints, and the exfiltration channel type | never | custom x-urlyze-fingerprint:value with kind = the type |
Behavioural rows carry a context field (e.g.
kind=download-execute; source=clipboard,
tool=ScreenConnect; onVendorHost=true,
file=ScreenConnect.ClientSetup.msi; fileVerdict=Malicious). In STIX it is the
indicator's description.
We do not derive domains from stager or delivery URLs. Attackers stage from shared platforms (code-hosting, file-sharing, app stores, RMM vendors' own clouds); a domain derived from those would block a service your users need. Block the full URL or the IP literal — that is what the lists carry.
Smart contracts (contract:evm)
EtherHiding stores its next stage in a smart contract and reads it through whichever public RPC node answers. Those nodes (Polygon and BNB Smart Chain public endpoints) are shared infrastructure; Urlyze never publishes them, in any tier, because blocking one blocks the chain for everyone. The contract is the attacker-owned artefact: pivot on it in a chain explorer or a threat-intel platform. It cannot be blocked by a firewall, which is why it is absent from every EDL list.
Only contracts read through a node whose chain Urlyze has verified are published — the chain id is part of the value. Stored scans made before this release carry no contract.
contextmay carryselector=0x<8 hex>— the 4-byte function selector the page called. It identifies the kit's lookup function, not a transaction.- STIX 2.1 has no object for a smart contract, so the indicator uses a custom one:
[x-urlyze-evm-contract:value = 'eip155:<chainId>:<address>'].
Operator accounts (rmm:operator, exfil:operator)
Some attacker infrastructure is an account on a legitimate service, not a host. A firewall cannot block it, so neither type is ever on an EDL list. Report it to the service so the account is closed.
rmm:operator: the remote-management account a delivered installer would join the victim's machine to.screenconnect:instance:names a vendor-hosted ScreenConnect tenant.screenconnect:relay:names a self-hosted relay, read from thehandpparameters of the client URL.fleetdeck:agent:carries the agent token. Report it to the RMM vendor's abuse desk.exfil:operator: the account that receives harvested data, as a Telegram bot id or a Discord webhook id. Only the public id is stored. The token's secret half is never stored or published, so the row cannot be used to post to the account. Report it to Telegram or Discord.- STIX:
[x-urlyze-rmm-operator:value = '…']and[x-urlyze-exfil-operator:value = '…'].
The STIX bundle
format=stix returns a graph, not a flat list of indicators. Besides the
identity (Urlyze) and one indicator per row, the bundle carries:
relationshiprelated-tofrom each scan'surlindicator (itsdomainindicator when it has no URL) to every other indicator of the same scan. The relationship'sdescriptionsays what the value was on the page, e.g. "The pasted command fetches its next stage from this URL."infrastructure, one per attacker-held key (see the table below). Its id depends only on the value, so every lure page that used the same key, on any page of the feed, links to the same node. The key's indicator points at it withindicates, and each lure page's indicator withrelated-to.attack-pattern, one per MITRE ATT&CK technique, with the technique id inexternal_references(source_name=mitre-attack,external_id=T…). Indicators point at it withindicates.
| attacker-held key | infrastructure_types |
|---|---|
smart contract (contract:evm) | command-and-control |
RMM account (rmm:operator) | command-and-control |
exfiltration account (exfil:operator) | exfiltration |
stager URL (url:stager) | hosting-malware |
| indicator | technique |
|---|---|
the clipboard command | T1204.004 (User Execution: Malicious Copy and Paste) |
stager and next hop (url:stager, url:next-hop) | T1105 (Ingress Tool Transfer) |
a delivery with a named RMM tool, and rmm:operator | T1219 (Remote Access Tools) |
a delivery with no named tool, and the delivered file (sha256:file) | T1204.002 (User Execution: Malicious File) |
the smart contract (contract:evm) | T1102.001 (Web Service: Dead Drop Resolver) |
Domains, URLs and page fingerprints carry no technique: they say where a page was, not what it did.
A withdrawn indicator stays in the bundle with revoked: true and takes part in no
relationship.
// one scan's lure URL → the ScreenConnect account its installer joins
{ "type": "relationship", "relationship_type": "related-to",
"source_ref": "indicator--…", // url: the lure page
"target_ref": "indicator--…", // rmm:operator
"description": "…" }
{ "type": "relationship", "relationship_type": "indicates",
"source_ref": "indicator--…", // rmm:operator
"target_ref": "infrastructure--…" } // same id on every page that used this account
{ "type": "infrastructure", "id": "infrastructure--…",
"infrastructure_types": ["command-and-control"], "name": "…" }
{ "type": "relationship", "relationship_type": "indicates",
"source_ref": "indicator--…", // rmm:operator
"target_ref": "attack-pattern--…" } // T1219
Tiers
By default the feed contains only indicators a human confirmed — your analysts'
Confirm malicious and the scans Urlyze analysts confirm for you. Two optional tiers
add what our scanner is certain about without a human in the loop. You choose per request
with tier.
tier |
what is in it | who decided | default routes | confidence |
|---|---|---|---|---|
confirmed (default) |
scans a human confirmed | a person | JSON, STIX, EDL | the analyst's, else 90 |
high |
scans our scanner marked Malicious and observed doing one of the things listed below | the scanner, on signals we measured at zero contradictions | JSON, STIX; EDL only when you ask (edl/*?tier=high) |
80 |
medium |
every other scan marked Malicious or Suspicious | the scanner | JSON and STIX only (?tier=all); never an EDL —
edl/*?tier=all is refused |
50 (Malicious) / 30 (Suspicious) |
What qualifies as high
- The page wrote a command to the visitor's clipboard that would execute something (ClickFix).
- The page delivered a file our file analyser convicted.
- The page delivered a remote-management installer with at least two signs of pretext (fake document, tenant label absent from the domain, and the like).
- Credentials the page collects are sent to a known exfiltration channel (Telegram, Discord, webhook.site …).
- The server exposes an open directory with a phishing-kit archive, a credential log, or source.
- The page matches a deployment a human already confirmed (structural fingerprint).
- A phishing-kit signature with escalation, or an anti-bot key an analyst already flagged — these three are rare enough that we have no measurement behind them yet; they are listed for completeness.
Correlation-rule matches alone do not qualify — one such rule contradicted itself within two
hours in our measurement, so those rows are medium.
Visibility
high and medium rows come from your own scans
(whatever their visibility) and from anonymous public scans on urlyze.io.
Another workspace's private scans never appear in your feed, in any tier. In practice
tier=high is a community feed of public scans plus your own.
Lifetime
Automated rows carry firstSeen (the scan) and lastSeen (the latest
scan of the same URL). A row leaves the feed 30 days after its last sighting;
there is no withdrawal event for automated rows. Confirmed rows keep the 90-day analyst expiry
and the explicit withdrawal.
Push
SIEM push (the signed webhook) stays confirmation-driven. Poll tier=high for the
automated tier; an incremental poll every 5–15 minutes with since is the intended
pattern. To be told when each of your own scans finishes, use the
scan.completed webhook instead.
Examples
# default: confirmed only (unchanged)
curl "https://api.urlyze.io/api/feed/iocs" -H "X-Api-Key: YOUR_API_KEY"
# confirmed + high, incremental
curl "https://api.urlyze.io/api/feed/iocs?tier=high&since=2026-09-01T00:00:00Z" -H "X-Api-Key: YOUR_API_KEY"
# everything, STIX, for a TIP that scores by confidence
curl "https://api.urlyze.io/api/feed/iocs?tier=all&format=stix" -H "X-Api-Key: YOUR_API_KEY"
# firewall list including the high tier (opt-in)
curl "https://api.urlyze.io/api/feed/edl/urls?tier=high&severity=malicious" -H "X-Api-Key: YOUR_API_KEY"
Envelope
tier:confirmed|high|medium— on every row.confidence: filled for automated rows (see table); the analyst's number on confirmed rows.confirmedFingerprintId: empty on automated rows;evidenceScanIdalways set.contexton automated rows names the signal:signal=captured-command|signal=convicted-delivered-file|signal=executable-delivery|signal=definitive-exfil|signal=open-directory|signal=confirmed-bad-match|signal=phishing-kit|signal=analyst-sitekey; behavioural rows keep their existing context after a;.- STIX:
confidenceset;labelscarriesurlyze:tier=<tier>and, on automated rows,urlyze:signal=<name>.