Malanta IOPA STIX/TAXII Feed - Integration Guide
Malanta IOPA STIX/TAXII Feed
TAXII 2.1 · STIX 2.1 · Pre-Attack Indicators · Clusters · Relationships
Version 1.0 | October 2026
Contents
- Overview
- Authentication
- Endpoints
- Pagination and incremental sync
- Recommended sync use cases
- STIX object model
- Platform setup examples
- Errors
- Limitations
- Quick reference
Overview
Malanta delivers pre-attack threat intelligence over TAXII 2.1 as STIX 2.1 objects. Use this guide to connect your TIP, SOAR, SIEM, or custom collector.
The feed exposes Malanta’s attack-infrastructure graph so you can:
- Ingest malicious / pre-attack indicators (primarily domains)
- See clusters of related attack infrastructure (
intrusion-set) - Follow relationships between indicators and clusters
Data is marked TLP:AMBER. Treat accordingly.
Support: support@malanta.ai
Base URL
Field | Value |
|---|---|
Discovery URL | |
API root | |
Protocol | TAXII 2.1 |
Content | STIX 2.1 |
Always start from the discovery URL. Discovery advertises the API root; do not hardcode alternate hosts unless Malanta tells you to.
Collection
Collection ID | Title | Purpose |
|---|---|---|
| Malanta Pre-Attack Indicators | Indicators, clusters, and indicator↔cluster relationships |
The collection is read-only (can_read: true, can_write: false).
Authentication
All requests require your Malanta API key. Keys are prefixed with malanta_. A missing or invalid key is rejected before the request reaches the service (401/403).
Supported transports (first match wins):
Priority | Method | Example |
|---|---|---|
1 |
|
|
2 | Bearer |
|
3 | HTTP Basic | Username: any string · Password: your API key |
Recommended: x-api-key.
Basic Auth is provided for platforms (for example ThreatQ) that only expose username/password fields. Put the API key in the password field; username can be anything non-empty.
Required headers
Accept: application/taxii+json;version=2.1
x-api-key: malanta_xxxxxxxxxxxxx
Responses use Content-Type: application/taxii+json;version=2.1 and include X-TAXII-Content-Type with the same media type.
Keep your API key private. Never expose it in frontend code, mobile apps, or public repositories.
Endpoints
Base: https://app.malanta.ai/feeds/taxii2
Trailing slashes are accepted. Prefer not relying on HTTP redirects; redirects can drop auth headers.
Discovery
Endpoint: GET /taxii2
Returns TAXII server information and available API roots.
Example request
curl --request GET \
--url 'https://app.malanta.ai/feeds/taxii2' \
--header 'Accept: application/taxii+json;version=2.1' \
--header 'x-api-key: malanta_xxxxxxxxxxxxx'API root
Endpoint: GET /taxii2/default
Returns API root metadata (supported versions, max content length).
List collections
Endpoint: GET /taxii2/default/collections
Returns available collections.
Example request
curl --request GET \
--url 'https://app.malanta.ai/feeds/taxii2/default/collections' \
--header 'Accept: application/taxii+json;version=2.1' \
--header 'x-api-key: malanta_xxxxxxxxxxxxx'Collection metadata
Endpoint: GET /taxii2/default/collections/{collection_id}
Path parameters
collection_id— usepre-attack-indicators
Example request
curl --request GET \
--url 'https://app.malanta.ai/feeds/taxii2/default/collections/pre-attack-indicators' \
--header 'Accept: application/taxii+json;version=2.1' \
--header 'x-api-key: malanta_xxxxxxxxxxxxx'Get objects
Endpoint: GET /taxii2/default/collections/{collection_id}/objects
Returns STIX 2.1 objects in a TAXII envelope (paginated).
Path parameters
collection_id— usepre-attack-indicators
Query parameters
Param | Type | Default | Meaning |
|---|---|---|---|
| integer |
| Page size. Max response object count is 2002 (page + Malanta identity + TLP:AMBER marking). |
| string | none | Opaque pagination token from the previous response |
| RFC 3339 | none | Only objects added after this time (incremental sync) |
Example request
curl --request GET \
--url 'https://app.malanta.ai/feeds/taxii2/default/collections/pre-attack-indicators/objects?limit=2000' \
--header 'Accept: application/taxii+json;version=2.1' \
--header 'x-api-key: malanta_xxxxxxxxxxxxx'Example response
{
"objects": [ /* STIX 2.1 SDOs / SROs */ ],
"more": true,
"next": "<opaque-token>"
}Response fields
objects— STIX objects for this pagemore—true→ call again withnext;false→ drain completenext— pagination token (present whenmoreis true)
Response headers
Header | Meaning |
|---|---|
| Earliest |
| Latest |
Use X-TAXII-Date-Added-Last as the next added_after value for incremental sync.
Paginated request
curl --request GET \
--url 'https://app.malanta.ai/feeds/taxii2/default/collections/pre-attack-indicators/objects?next=<token>' \
--header 'Accept: application/taxii+json;version=2.1' \
--header 'x-api-key: malanta_xxxxxxxxxxxxx'Manifest
Endpoint: GET /taxii2/default/collections/{collection_id}/manifest
Returns object metadata without full STIX bodies. Same query parameters as Get objects (limit, next, added_after).
Pagination and incremental sync
Full drain (initial load)
1. GET .../objects?limit=2000
2. While response.more == true:
GET .../objects?next=<token>
3. When more == false → collection fully drained for this cycle
Do not keep polling the final token. When more is false, the cycle is finished.
Incremental sync (deltas)
Start a new request with added_after set to the last watermark you observed (typically X-TAXII-Date-Added-Last from the previous successful sync):
curl --request GET \
--url 'https://app.malanta.ai/feeds/taxii2/default/collections/pre-attack-indicators/objects?limit=2000&added_after=2026-04-01T12:00:00.000Z' \
--header 'Accept: application/taxii+json;version=2.1' \
--header 'x-api-key: malanta_xxxxxxxxxxxxx'Then follow next until more is false, same as a full drain.
added_after must be valid RFC 3339. Invalid values return HTTP 400 with a TAXII error resource.
Object order
Within one drain cycle, objects arrive in this order across pages:
- Indicators (reputation / observables)
- Clusters (
intrusion-set) - Relationships (indicator → cluster, type
indicates)
A page never mixes streams; handoff to the next stream happens on the next HTTP call via next.
Recommended sync use cases
Pick one of these patterns for first-time onboarding, then stay on incremental API pulls. Do not invent a hybrid that starts mid-history with a date filter.
Important — clustering completeness. Your first load of the graph must be a complete baseline: either a Malanta full dump, or a TAXII drain with no added_after (empty date).
If the first TAXII call uses a specific added_after (for example “last 7 days”), you can lose clustering data. Older intrusion-set / relationship objects fall outside the window, while newer indicators may still appear — your TIP ends up with orphan indicators and a broken cluster graph. Only use added_after after a successful full baseline.
Use case A — Full dump, then API intervals
Best when: you need historical / inactive indicators as well as the live active set, and the full cluster graph in one shot.
Step | What | Notes |
|---|---|---|
1 | Request a full feed dump from Malanta | Includes active and inactive indicators, clusters, and relationships (baseline graph) |
2 | Load the dump into your TIP / store | Treat this as the complete starting state |
3 | Record a watermark | Use dump generation time, or run one TAXII drain and store |
4 | Switch to TAXII incremental sync |
|
5 | Follow | Same pagination rules as above |
[Full dump: active + inactive + clusters + relationships]
↓
load once into TIP
↓
[TAXII] ?added_after=<watermark> → daily / hourly intervals
Contact your Malanta account team or support@malanta.ai to obtain the dump and confirm the handoff watermark.
Use case B — API full active set, then daily intervals
Best when: you only need the currently active feed via TAXII and do not require inactive history from a dump.
Step | What | Notes |
|---|---|---|
1 | TAXII full drain without |
|
2 | Ingest all returned objects | Active indicators + clusters + relationships (complete active graph) |
3 | Persist watermark | Save |
4 | Daily (or hourly) incremental | New cycle: |
[TAXII] no added_after → full active baseline (indicators + clusters + relationships)
↓
persist X-TAXII-Date-Added-Last
↓
[TAXII] ?added_after=<watermark> → every day (or chosen interval)
What not to do
Anti-pattern | Why it fails |
|---|---|
First sync with | Misses older clusters/relationships → incomplete clustering |
Skipping relationships / clusters and ingesting only indicators | Loses Malanta’s attack-infrastructure graph |
Keeping the final | Incorrect vs TAXII finite-drain model; use a fresh request each interval |
STIX object model
Every page begins with a short preamble:
- Malanta
identity - TLP:AMBER
marking-definition
Then the page’s feed objects.
Indicator
Field | Notes |
|---|---|
|
|
|
|
| Observable value (e.g. the domain or IP string) |
| STIX pattern, e.g. |
|
|
| Always |
| 0–100 from Malanta |
| Classification and provenance tags — see Labels |
| First observed time ( |
| Last classified / updated time ( |
| Same as |
| Not set on this TAXII feed today (no expiry emitted) |
| Points at TLP:AMBER |
| Malanta identity |
Supported observable types in patterns: domain-name, ipv4-addr, url, file (SHA-256).
Labels
Every indicator carries STIX labels so you can filter and prioritize in your TIP. Labels come from two places: always added by the feed, and passed through from Malanta’s reputation data when present.
Classification (always one of these)
Label | Meaning |
|---|---|
| Indicator of Pre-Attack — Malanta’s core signal. Infrastructure observed during adversary setup (resource development), often weeks before it appears in classic IoC feeds. Always paired with |
| Indicator of Compromise — known malicious infrastructure already reported by the broader threat-intel ecosystem. Malanta redistributes and correlates it into the same cluster graph. |
Always added by the feed
Label | Meaning |
|---|---|
| Marker that the object is attack infrastructure (not a post-compromise host artifact alone). |
Observable type | Raw type from the feed, e.g. |
| Always on IOPA. On IOC, only if a provider name is |
| On IOC — one label per external provider (e.g. |
| On IOC when at least one provider is not Malanta — Malanta is redistributing an external indicator. |
APT attribution (apt:…) — when available
Label | Meaning |
|---|---|
| Linked APT / threat-actor name when Malanta has attribution for that indicator (e.g. |
- APT is returned only via labels — there is no separate STIX APT field on indicators or clusters in this feed.
- Not every indicator has APT tags. If none are known,
apt:…is simply omitted. - Comma-joined values are split so TIP filters can match exactly:
apt:APT17,APT5→apt:APT17+apt:APT5. - Use
apt:…for hunting, TIP tagging, and (with OpenCTI) connectors that map these labels to Intrusion Sets / Threat Actors.
Other pass-through labels (when available)
Upstream reputation labels are forwarded after normalization. Besides IOC / IOPA / apt:…, you may also see free-form or prefixed tags such as:
Label (examples) | Meaning |
|---|---|
| Category / technique tag from Malanta classification (example; not always present). |
| Additional taxonomy tokens when present upstream. Comma-joined |
Treat unknown labels as opaque tags — keep them for filtering; do not drop the indicator because a label is unfamiliar.
Cluster labels (on intrusion-set)
Clusters use a fixed label set (not the indicator taxonomy above):
Label | Meaning |
|---|---|
| Cluster represents related attack infrastructure. |
| Cluster is authored by Malanta. |
How to use this as a customer
- Prefer
IOPAwhen you want Malanta’s early / predictive coverage (pre-attack). - Prefer
IOCwhen you want confirmed malicious indicators with external provenance. - Many SOCs ingest both: IOPA for proactive blocking / hunting, IOC for correlation with existing TI feeds.
- Use
source:…to attribute origin; useapt:…when present for actor-focused triage. - Do not drop either class solely because the word “IOC” or “IOPA” is unfamiliar — both are first-class objects in this feed.
Cluster (intrusion-set)
A Malanta cluster is a group of related attack-infrastructure indicators that share ownership, certificates, or other linkage. In STIX it is published as an intrusion-set.
Field | Notes |
|---|---|
|
|
|
|
| Short stable display name derived from the cluster id (not a human campaign title) |
| Always |
| Fixed set: |
| When Malanta first built the cluster ( |
| When the cluster was last updated ( |
| Points at TLP:AMBER |
| Malanta identity |
How to use this as a customer
- Treat each
intrusion-setas a cluster of related infrastructure, not necessarily a named APT group. - Join indicators to clusters via relationships (below) — do not invent joins from indicator labels alone.
- Keep clusters even if you primarily alert on indicators; without them the graph (and
indicatesedges) is incomplete.
Relationship
Edges that connect indicators to clusters. In this feed every relationship is type indicates: the indicator points at the intrusion-set it belongs to.
Field | Notes |
|---|---|
|
|
|
|
| Always |
| STIX id of the indicator ( |
| STIX id of the cluster ( |
| When Malanta first recorded the membership ( |
| When the membership was last updated ( |
| Points at TLP:AMBER |
| Malanta identity |
Direction
indicator --indicates--> intrusion-set (cluster)How to use this as a customer
- Resolve
source_ref/target_refagainst objects already ingested (or pending in the same drain). - Because object order is indicators → clusters → relationships, a full drain without
added_after(or a dump baseline) keeps refs resolvable. - STIX IDs for indicators, clusters, and relationships are deterministic (stable across days), so TIP snapshot diffs stay clean.
Platform setup examples
OpenCTI
- Data → Connectors (or Ingestion) → add a TAXII 2 connector / feed.
- Discovery URL:
https://app.malanta.ai/feeds/taxii2 - Auth: API key via
x-api-keyor Bearer, depending on connector fields. - Select collection
pre-attack-indicators. - Enable periodic sync. Prefer connectors that honor
added_after/ date-added headers for deltas.
OpenCTI expects a finite drain (more: false) then a new poll with a time filter — that matches Malanta’s behavior.
ThreatQ
- TAXII URL:
https://app.malanta.ai/feeds/taxii2 - Username: your org name or any placeholder
- Password: your full
malanta_…API key - Collection:
pre-attack-indicators
Python
import httpx
BASE = "https://app.malanta.ai/feeds"
HEADERS = {
"Accept": "application/taxii+json;version=2.1",
"x-api-key": "malanta_xxxxxxxxxxxxx",
}
def drain_collection(collection_id: str, *, added_after: str | None = None):
params: dict = {"limit": 2000}
if added_after:
params["added_after"] = added_after
url = f"{BASE}/taxii2/default/collections/{collection_id}/objects"
watermark = added_after
with httpx.Client(headers=HEADERS, timeout=120.0) as client:
while True:
r = client.get(url, params=params)
r.raise_for_status()
body = r.json()
yield from body.get("objects") or []
last = r.headers.get("X-TAXII-Date-Added-Last")
if last:
watermark = last
if not body.get("more"):
break
params = {"next": body["next"]} # do not re-send limit/added_after with next
return watermark
cURL smoke test
curl --request GET \
--url 'https://app.malanta.ai/feeds/taxii2' \
--header 'Accept: application/taxii+json;version=2.1' \
--header 'x-api-key: malanta_xxxxxxxxxxxxx'Expected: JSON with title, default, and api_roots.
curl --request GET \
--url 'https://app.malanta.ai/feeds/taxii2/default/collections/pre-attack-indicators/objects?limit=10' \
--header 'Accept: application/taxii+json;version=2.1' \
--header 'x-api-key: malanta_xxxxxxxxxxxxx'Errors
Status | When | What to do |
|---|---|---|
| Bad query (e.g. invalid | Fix parameter; do not reuse a corrupt token |
| Missing or invalid API key / plan | Check key; contact Malanta |
| Unknown API root or collection | Confirm path and collection ID |
| Rate limited | Back off and retry |
| Transient server error | Retry with backoff |
TAXII error bodies follow TAXII 2.1 error resources, for example:
{
"title": "Invalid Query Parameter",
"description": "...",
"http_status": "400"
}Limitations
- Collection object-by-id lookup resolves the Malanta identity and TLP:AMBER marking. Individual feed indicators / clusters / relationships are consumed via the paginated
/objectsstream. match[…]query filters may be accepted by the router; rely on collection choice +added_afterfor supported filtering unless Malanta confirms additional match support for your account.- Feed is pull-only (no TAXII write / inbox).
Quick reference
- Discovery:
https://app.malanta.ai/feeds/taxii2 - API root:
https://app.malanta.ai/feeds/taxii2/default - Collection:
pre-attack-indicators - Auth:
x-api-key: malanta_xxxxxxxxxxxxx(also Bearer / Basic) - Accept:
application/taxii+json;version=2.1 - Baseline (once): full dump (active+inactive) or TAXII with no
added_after(active only) - Then intervals:
GET objects?added_after=<watermark>→ whilemore: follownext→ stop - Always baseline first before any dated incremental — otherwise clustering can break
- Respect TLP:AMBER — do not redistribute outside your organization without agreement
Questions or onboarding help: support@malanta.ai
Updated on: 13/10/2026
Thank you!