Automation and MISP API
Use the MISP REST API to search intelligence, maintain events, exchange observations and connect detection or response tools. Start with a small read-only query, check the returned scope and schema, then add writes or scheduled processing deliberately.
Find the API reference
Open API → OpenAPI (/api/openapi) for the bundled reference, or download its specification from /doc/openapi.yaml. Use API → REST client (/api/rest) to load request templates, edit a request and inspect its response. The templates depend on the logged-in user’s role.
The REST client sends an HTTP request with the headers you supply. An interactive login does not replace the request’s authentication key: replace YOUR_API_KEY in its Headers tab. Keep Skip SSL validation off. Check the method, target URL and body before choosing Send; the client can perform writes as well as searches. Its response panel shows status, headers and body.
Use GET /servers/getVersion for the running server version and recommended PyMISP version. The OpenAPI document’s own info.version is not a substitute for that server-version check. For new development-branch features, check the relevant controller and its constraints as well as the reference. Use PyMISP for Python integrations where it provides a helper.
Configure an automation account
Use a dedicated account whose organisation and role match the integration’s purpose. An authentication key inherits that account’s permissions and data visibility; it does not grant site-administrator access by itself. A detection exporter normally needs read access, while an ingestion service needs the relevant add/modify permissions. Native MISP synchronization uses its own sync-account setup.
Open Account → My Profile → Auth keys. Choose Add auth key, enter a useful comment and set an expiry, allowed source IPs and Read only as appropriate. IP restrictions are evaluated against the source address seen by MISP, including the effects of an authorised reverse proxy.
The new key is displayed once. Store it in the integration’s secret store; do not commit it to source control or paste it into shared request logs. Editing an existing key changes its restrictions, not the account’s role. Rotate by deploying a new key, verifying the integration, then revoking the old key. See account management.
When IP usage logging is enabled and your role allows it, a key’s details can show Seen IPs and a Pin action. Pinning restricts the key to that address; confirm it is the stable egress address of the intended service before applying it.
Headers and TLS
Send the raw key, without a Bearer prefix, in the Authorization header:
Authorization: YOUR_API_KEY
Accept: application/json
Content-Type: application/json
Use HTTPS and keep certificate verification enabled. For a private certificate authority, configure the client’s CA bundle (curl --cacert or Requests’ verify path) rather than disabling verification. Keep the key out of URLs. A read-only key may perform supported read-only POST searches; the HTTP verb alone does not determine whether an operation writes data.
Make a first request
Set MISP_URL to your instance’s base URL without a trailing slash. Supply MISP_KEY through your secret-management mechanism. In an interactive Bash session, a hidden prompt is another option:
export MISP_URL='https://misp.example.org'
read -r -s -p 'MISP API key: ' MISP_KEY
export MISP_KEYThe following helper keeps the headers consistent. It leaves TLS verification enabled and reports HTTP errors while retaining their response bodies for diagnosis:
misp_api() {
local method="$1" path="$2"
shift 2
curl --silent --show-error --fail-with-body \
--request "$method" \
--header "Authorization: ${MISP_KEY}" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
"${MISP_URL%/}${path}" "$@"
}
misp_api GET /servers/getVersionCheck version, pymisp_recommended_version and any capabilities relevant to your integration, such as collection_sync. Capability flags and the account’s role matter alongside the version. Do not infer support for every MISP 3.0 target type from the PyMISP package’s major version.
Search events and attributes
For a worked comparison with the browser interface, see Searching and Filtering Intelligence.
Use POST /events/restSearch for event containers and POST /attributes/restSearch for individual observations. Put filters in a JSON body, not positional URL segments. For example, save this as search.json to find occurrences of a reserved training domain:
{
"returnFormat": "json",
"value": "example.org",
"type": "domain",
"limit": 100,
"page": 1,
"includeEventUuid": true
}misp_api POST /attributes/restSearch --data @search.jsonThe JSON attribute result is normally under response.Attribute. An event search normally returns a response list of Event envelopes. Other endpoints and export formats use different shapes: check them before writing a generic parser. IDs are local to an instance; retain UUIDs for event and attribute identity across systems.
Choose the filters for the job
| Filter | Meaning |
|---|---|
value, type, category |
Select the observation values and types you need. Wildcard and boolean forms are endpoint-specific. |
eventid, uuid |
Restrict the search to known events or entities. Prefer UUIDs for portable references. |
tags |
Select handling, source or workflow tags. Tags do not grant access or replace distribution. |
published |
Select published/unpublished events; omit it when both are intended and visible. Unpublished is not a privacy setting. |
to_ids |
Use true to select detection attributes. Omitting the filter is useful for investigation and context searches. |
enforceWarninglist |
Exclude warninglist matches from an export where supported. A missing match is not a guarantee of safety. |
timestamp |
Filter modifications in the controller’s scope: event changes for event searches, attribute changes for attribute searches. |
publish_timestamp |
Filter event publication times. The legacy last parameter is an alias; use the explicit name in new clients. |
from, to |
Filter the event’s date, not necessarily the last modification or observation time. |
deleted |
On attribute searches, request soft-deleted records where the account may see them; [0,1] includes active and deleted records. |
limit, page |
Bound the result and request subsequent pages, starting at page 1. |
withAttachments |
Include binary attachment data where supported; leave it off for ordinary indicator polling. |
For a detection export, combine a deliberate source/audience selection with published: true, to_ids: true and suitable warninglist handling. The training data in this book has to_ids disabled and is not intended for detection. Check best practices before enabling an operational export.
Keep feed history while limiting detection exports
Keep older imported observations in MISP for investigation and correlation, while applying a shorter lifetime to the data sent to a SIEM or NIDS. The following two-query policy includes all otherwise eligible data outside a tagged feed, plus that feed’s recently published events. It filters the export without deleting source records or changing their IDS or correlation flags.
The screenshots in this recipe use MISP 3.0.0, 3.0_wip commit 0aaeb964b, in light mode. They show staged requests with an authorization placeholder, not submitted exports or detector results. See the capture record for revision and fixture details.
- Ask a site administrator to set the feed’s Default tag to a dedicated label such as
expireMeInAMonth; see Managing Feeds. If a Default tag collection is configured, include the label in that collection, which takes precedence over the single default tag. This example label is a local policy marker, not a built-in expiration action. Check the event-level tag after importing, and review/tag previously imported events separately: changing the feed definition does not immediately retag historical records. Keep correlation enabled and review any Delta merge or other deletion policy separately if history must remain available. - Configure the exporting account and the intended source/audience scope using the API setup. Apply the same baseline filters to both requests below. These examples select published, IDS-enabled attributes; add your approved source/type and warninglist restrictions to both as needed.
- Save the first body as
regular-data.jsonto select eligible attributes without the policy tag, with no time cutoff:
{
"returnFormat": "json",
"published": true,
"to_ids": true,
"tags": {"NOT": ["expireMeInAMonth"]},
"limit": 100,
"page": 1
}- Save the second body as
recent-feed.jsonto select eligible attributes with that tag, restricted to events published in the past 30 days:
{
"returnFormat": "json",
"published": true,
"to_ids": true,
"tags": {"OR": ["expireMeInAMonth"]},
"publish_timestamp": "30d",
"limit": 100,
"page": 1
}For a scheduled export, compute the absolute publication cutoff once at the start of the run, before requesting any pages. For example, replace the relative value in recent-feed.json with a fixed Unix timestamp:
python3 - <<'PY'
import json
import time
from pathlib import Path
path = Path("recent-feed.json")
query = json.loads(path.read_text())
query["publish_timestamp"] = int(time.time()) - 30 * 24 * 60 * 60
path.write_text(json.dumps(query, indent=2) + "\n")
PYReuse this same numeric cutoff on every page and retry in that run. Resolving "30d" separately for each request can shift the selected set as events age out. A fixed cutoff avoids that moving time boundary; it does not make pagination over concurrently modified data an immutable snapshot.
In API → REST client, choose POST, enter /attributes/restSearch, and paste each body in the request editor. The first request omits the policy tag and has no time cutoff:
The second includes the tag and the run’s fixed publication cutoff:
Use your own key when actually sending the requests, following authentication and headers. For a script, use the request helper to send each body to /attributes/restSearch:
misp_api POST /attributes/restSearch --data @regular-data.json
misp_api POST /attributes/restSearch --data @recent-feed.jsonRequest every page of each result, then feed their union to the destination. Preserve attribute UUIDs for reconciliation; if the destination stores distinct values or rules, handle duplicate values across occurrences before removing a detection still supported by another eligible occurrence. An untagged eligible attribute remains in the regular set regardless of age; a tagged one is included only while its event passes the publication cutoff. The tag filter can also match attribute tags, so reserve this label for the intended feed policy and inspect where it is attached.
The historical recipe uses "last": "30d"; last is an alias for event publication time, so the example uses the explicit publish_timestamp name. It does not mean imported in the past 30 days, last observed in the past 30 days, or created in the past 30 days. Republishing an old event can bring all of its otherwise eligible attributes back into the recent set. This matters especially for a Fixed event feed that republishes one growing event on every pull. Use an observation-time policy or an appropriate decaying model when publication recency does not express the lifetime you need.
Rebuild or reconcile the destination’s managed set on each scheduled run, withdrawing entries that no longer belong to either result. A connector that only appends new indicators will retain expired detections even though later exports omit them. Finish both queries and all pages successfully before replacing the destination’s set; a failed request is not an empty selection. Check the union and withdrawals with the destination’s actual account and export format, as described in incremental polling and recovery and consumer verification. Older source observations remain searchable in MISP and available for correlation under the instance’s access and correlation settings.
The tag attachment and publication-time filter semantics above were checked against the MISP 3.0_wip feed importer and timestamp filter implementation at commit 0aaeb964b. Validate the complete policy on your instance before connecting it to operational detection.
Poll incrementally and recover
A reliable connector needs more than a recurring search:
- Keep a checkpoint of the last fully processed time window. Query with an overlap so that equal timestamps, retries and delayed changes can be handled again.
- Use a bounded window and paginate through every result. Apply records by UUID and compare their modification time, so a retry does not create duplicate target records.
- Process relevant deletions, expiry and changes to detection flags. A value that stops matching a filter or becomes invisible may disappear without a tombstone in that query.
- Commit the checkpoint only after the whole batch is safely applied. On failure, retry from the previous checkpoint with bounded backoff.
- Reconcile periodically against the authorised source set. Pagination over a changing data set is not an immutable snapshot, and a modification filter alone cannot report every access revocation.
Create and maintain event data
The following bodies illustrate writes to a private, unpublished exercise event. Check the response for the created UUID and use that identifier in later requests. The account needs the corresponding permissions and ownership; administrator credentials should not be a script default.
Create an event
POST to /events/add:
{
"Event": {
"info": "Integration exercise: private observations",
"date": "2026-11-06",
"distribution": 0,
"threat_level_id": 4,
"analysis": 0,
"published": false
}
}threat_level_id is 1 High, 2 Medium, 3 Low, 4 Undefined. Do not use a zero-based mapping. Distribution 0 restricts the event to its owning organisation; an unpublished community event can still be visible locally under the instance’s policy.
Add an attribute or object
POST to /attributes/add/{event_uuid} with an Attribute envelope:
{
"Attribute": {
"type": "ip-dst",
"category": "Network activity",
"value": "192.0.2.80",
"to_ids": false,
"distribution": 5,
"comment": "Reserved documentation address; keep out of detection exports."
}
}Distribution 5 on a child means inherit from its parent. Use GET /attributes/describeTypes to inspect accepted types/categories and POST /objects/add/{event_uuid} for a structured MISP object. Follow the active object template; a collection of similarly named standalone attributes does not create an object automatically.
MISP 3.0 values and research data
MISP 3.0 distinguishes an attribute occurrence from the value it holds. An attribute has a UUID; a value has a SHA-256 address computed from the stored, normalized UTF-8 text.
Look up an accessible value with:
misp_api GET /values/view --get --data-urlencode 'value=example.org'The equivalent address lookup is GET /values/view/sha256:{hex}. Local numeric value IDs are not portable identifiers. Read the stored value and address from the response rather than hashing an unnormalized input. For a composite attribute, each component value has its own address.
Use the address fields appropriate to the target type:
| Target | Address in a write |
|---|---|
| Event, attribute, object or another UUID target | object_uuid for Analyst Data; element_uuid for collection elements. |
AttributeValue |
object_sha256 for Analyst Data; element_sha256 for collection elements. |
| Relationship’s value target | related_object_type: "AttributeValue" with related_object_sha256. |
For example, POST a note to /analystData/add/Note/{sha256}/AttributeValue:
{
"Note": {
"note": "Training value: retain the source context before making an assessment.",
"language": "en",
"distribution": 0
}
}Add a value reference with POST /collectionElements/add/{collection_id}:
{
"CollectionElement": {
"element_type": "AttributeValue",
"element_sha256": "SHA256_OF_STORED_VALUE",
"description": "Reserved training value, not a maliciousness verdict."
}
}These records have their own distribution. A reference does not give its reader access to the source event or reveal a value that the reader cannot resolve through a visible attribute. See Analyst Data and Collections for visibility and older-peer limitations. Some 3.0 calls are not wrapped by every PyMISP release; use documented REST requests for those operations rather than inventing a client method.
The old /threads/ and /posts/ discussion API is removed in this branch. Use Analyst Data notes/replies on the relevant target. Event reports use /eventReports/ endpoints for a narrative; neither a note nor a report should be treated as an attribute containing a detection indicator.
Sightings API
POST to /sightings/add/{attribute_uuid} to record an observation of that occurrence:
{
"type": 0,
"source": "Documentation exercise",
"timestamp": 1793956200
}Use 0 for a sighting, 1 for a false positive and 2 for an expiration. The add request uses timestamp; a returned record has date_sighting. Omitting the observation time records the current time. Supplying a value instead of one attribute occurrence can affect multiple matching attributes; choose the scope deliberately.
POST /sightings/restSearch provides filtered retrieval. For bulk input, use the endpoint’s documented batch format rather than repeatedly reporting the same observation after an uncertain response. See Sightings for interpretation and visibility.
Exports, attachments and specialised APIs
Use returnFormat in a restSearch body for exports. JSON is a useful starting point; CSV, text, STIX, Suricata/Snort, RPZ and other formats have different controller support and options. Inspect the live reference for the exact format and filters. Export success does not mean that every source feature is representable in the target format. Some formats, including text, default to published, IDS-enabled attributes when those filters are omitted. Set the intended scope explicitly; JSON and detection-oriented formats do not necessarily have identical defaults.
| Task | Current approach |
|---|---|
| JSON/XML event export | Search /events/restSearch with the selected format and bounded filters. |
| CSV or plain values | Search /attributes/restSearch with returnFormat: "csv" or "text", selecting the required types and fields. |
| IDS, hash/HIDS or DNS RPZ output | Select the supported format, source scope, detection flags and format-specific parameters explicitly. |
| STIX export/import | Use the supported STIX version and installed conversion dependencies; validate the output with the receiving platform. |
| Download an attachment | Use the attribute’s attachment-download endpoint, subject to event visibility. Request binary data only when needed. |
| Sample lookup/download | Use the documented sample APIs and retain the expected archive/password handling. Do not treat the response as ordinary indicator JSON. |
| Upload a file/sample | Use an attribute/object attachment workflow or the documented upload API. Its base64 payload, distribution and IDS defaults need review. |
| Proposals | /shadow_attributes/view, /add, /edit, /accept and /discard, with the required target identifier and ShadowAttribute envelope. The owner reviews proposed changes. |
| Sharing groups | GET /sharing_groups/index returns groups visible to the authenticated account. Their membership is separate from collections of content. |
| Feed metadata and jobs | /feeds/ endpoints described below; caching and ingestion are separate. |
| Organisations and users | Use the reference’s organisation and admin-user endpoints, with the appropriate administrator role and scope. Numeric IDs differ between instances. |
| Warninglists, statistics and type metadata | Use their documented read endpoints; access and response shape depend on the operation and role. |
The old positional export recipes (/events/xml/..., /events/csv/..., /events/text/... and similar paths) are legacy interfaces. Prefer a named JSON search body for new clients. Never copy an old URL recipe that embeds an authentication key.
Automate feed operations
Feed administration requires a site administrator. Typical operations are:
| Method and endpoint | Effect |
|---|---|
POST /feeds/enable/{id} |
Enable ingestion for that feed. |
POST /feeds/disable/{id} |
Disable ingestion; caching is controlled separately. |
POST /feeds/cacheFeeds/{id} |
Cache that feed when its caching flag is enabled. |
POST /feeds/cacheFeeds/all |
Cache all eligible feeds; use misp or freetext for a format scope. |
POST /feeds/fetchFromFeed/{id} |
Import an enabled feed. |
POST /feeds/fetchFromAllFeeds |
Bulk ingestion; review the source set before running it. |
With background jobs enabled, acceptance of a request does not prove the job completed. Inspect the job outcome, cache time or resulting event. See Managing feeds.
Query an enrichment service
GET /modules/index exposes enabled enrichment modules available to the account when the service is configured and reachable. POST /modules/queryEnrichment is the hover-query path: it requires an enabled module supporting hover and input matching that module’s schema. It is not a generic import, expansion or AI endpoint. The request needs a module name; MISP adds configured module credentials before forwarding it to the service.
Use the Modules chapter for service configuration, organisation restrictions, event import/enrichment and the separate AI family. Review results before saving them into intelligence or enabling their IDS flags.
Errors and operational checks
| HTTP result | What to check |
|---|---|
| 200 | Parse the expected envelope and inspect endpoint-level success/error fields. A job may still be pending. |
| 403 | Missing/invalid/expired key, IP restrictions, account role, read-only restriction or insufficient access. |
| 404 | Wrong route, missing entity or an entity not visible to this account. Do not interpret every 404 as a source deletion. |
| 405 | Method, required input or workflow constraint rejected. MISP also uses this status for some validation errors. Read the body. |
| 429 | Respect a rate limit and any retry guidance from the instance or gateway. |
| 5xx / transport failure | Bound retries, retain the last successful checkpoint and investigate server/job logs. Reconcile an ambiguous write before retrying it. |
Log status, endpoint, timing and stable identifiers rather than entire authenticated requests or sensitive response bodies. Test with the same account, filters and network path the scheduled integration will use. Include empty results, pagination, permission failures and source removals in acceptance checks, not only the first successful search.



