urlyze docs

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"
Point your firewall at the EDL URL with the 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.

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.

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:

attacker-held keyinfrastructure_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
indicatortechnique
the clipboard commandT1204.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:operatorT1219 (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

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