{
  "openapi": "3.1.0",
  "info": {
    "title": "CTI Forecast API",
    "version": "1.0.0",
    "summary": "Sourced, scored threat indicators and CVE exploitation forecasts.",
    "description": "Every indicator links to the research publication or curated feed that cites it. Free plan: CVE and SHA-256 only, data delayed by 24 h, no source references. Quota resets at midnight UTC. Error messages follow the Accept-Language header (fr or en). Free-text fields such as `reasons` are currently in French.",
    "contact": {
      "name": "CTI Forecast",
      "email": "contact@ctiforecast.com",
      "url": "https://ctiforecast.com/en/docs"
    },
    "termsOfService": "https://ctiforecast.com/en/terms"
  },
  "servers": [
    {
      "url": "https://ctiforecast.com"
    }
  ],
  "security": [
    {
      "ApiKeyHeader": []
    },
    {
      "BearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Indicators"
    },
    {
      "name": "Forecast"
    },
    {
      "name": "Account"
    }
  ],
  "paths": {
    "/v1/iocs": {
      "get": {
        "tags": [
          "Indicators"
        ],
        "operationId": "listIndicators",
        "summary": "Indicator feed, newest first",
        "description": "Paginate backwards with `until` = `next_until` from the previous page; collect incrementally with `since`.",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Comma-separated indicator types. Default: every type in your plan (Free: cve,sha256).",
            "schema": {
              "type": "string"
            },
            "example": "ip,domain"
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "Only indicators last seen after this ISO 8601 timestamp.",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "example": "2026-11-06T00:00:00"
          },
          {
            "name": "until",
            "in": "query",
            "required": false,
            "description": "Only indicators last seen before this timestamp (pagination).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "min_confidence",
            "in": "query",
            "required": false,
            "description": "Minimum confidence score (0-100). 70 gives a low-noise feed.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 100,
              "default": 0
            },
            "example": 70
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Results per page. Maximum: Free 100, Pro 1,000, Business 5,000.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IndicatorPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/v1/lookup": {
      "get": {
        "tags": [
          "Indicators"
        ],
        "operationId": "lookupIndicator",
        "summary": "Check one value (hash, IP, domain, URL, CVE)",
        "description": "CTI Forecast does not store the values you look up; only a daily request count per account is kept.",
        "parameters": [
          {
            "name": "value",
            "in": "query",
            "required": true,
            "description": "Value to check.",
            "schema": {
              "type": "string"
            },
            "example": "185.177.93.120"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LookupResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/v1/stats": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getStats",
        "summary": "Feed volume and your quota",
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Stats"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/v1/blocklist.txt": {
      "get": {
        "tags": [
          "Indicators"
        ],
        "operationId": "getBlocklist",
        "summary": "Plain-text blocklist, one value per line",
        "description": "For firewalls (pfSense, OPNsense), DNS filters, Wazuh CDB lists and MISP freetext feeds. Tools that cannot send headers may pass the key as `?key=` (keep such URLs private). IP lists never go below confidence 40.",
        "security": [
          {
            "ApiKeyHeader": []
          },
          {
            "BearerAuth": []
          },
          {
            "ApiKeyQuery": []
          }
        ],
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Indicator type.",
            "schema": {
              "type": "string",
              "enum": [
                "ip",
                "domain",
                "url",
                "sha256",
                "sha1",
                "md5"
              ],
              "default": "ip"
            }
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "Only indicators seen in the last N days.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 30
            }
          },
          {
            "name": "min_confidence",
            "in": "query",
            "required": false,
            "description": "Minimum confidence score.",
            "schema": {
              "type": "integer",
              "default": 70
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One value per line",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/Remaining"
              }
            },
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/v1/trending": {
      "get": {
        "tags": [
          "Indicators"
        ],
        "operationId": "listTrending",
        "summary": "Indicators whose mentions are accelerating",
        "description": "Last 7 days compared with the previous 7 days, recomputed hourly. Free plan: top 10.",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Comma-separated indicator types. Default: every type in your plan (Free: cve,sha256).",
            "schema": {
              "type": "string"
            },
            "example": "ip,domain"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Comma-separated statuses.",
            "schema": {
              "type": "string",
              "default": "nouveau,en_hausse"
            },
            "example": "nouveau,en_hausse"
          },
          {
            "name": "min_confidence",
            "in": "query",
            "required": false,
            "description": "Minimum confidence score.",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum results.",
            "schema": {
              "type": "integer",
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrendingPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/v1/forecast": {
      "get": {
        "tags": [
          "Forecast"
        ],
        "operationId": "listForecast",
        "summary": "CVEs ranked by probability of confirmed exploitation within 30 days",
        "description": "Combines EPSS with mention dynamics across security research; retrained weekly. Measured accuracy: https://ctiforecast.com/en/performance. Free plan: top 10, no references.",
        "parameters": [
          {
            "name": "signal",
            "in": "query",
            "required": false,
            "description": "Comma-separated signals: exploitee, imminente, a_surveiller, faible.",
            "schema": {
              "type": "string",
              "default": "imminente,a_surveiller"
            }
          },
          {
            "name": "min_score",
            "in": "query",
            "required": false,
            "description": "Minimum forecast score (0-100).",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum results.",
            "schema": {
              "type": "integer",
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForecastPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/v1/cve/{cve_id}": {
      "get": {
        "tags": [
          "Forecast"
        ],
        "operationId": "getCve",
        "summary": "Full record for one CVE",
        "parameters": [
          {
            "name": "cve_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^CVE-\\d{4}-\\d{4,7}$"
            },
            "example": "CVE-2024-3094"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/CveForecast"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "cve": {
                          "type": "string"
                        },
                        "found": {
                          "const": false
                        },
                        "in_kev": {
                          "type": "boolean"
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/v1/stix": {
      "get": {
        "tags": [
          "Indicators"
        ],
        "operationId": "getStixBundle",
        "summary": "STIX 2.1 bundle (Pro and Business)",
        "description": "Indicators with TLP:GREEN marking, valid_until and source references. Same filters as /v1/iocs. A read-only TAXII 2.1 server is also available at /taxii2/ (Basic auth, password = API key).",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Comma-separated indicator types. Default: every type in your plan (Free: cve,sha256).",
            "schema": {
              "type": "string"
            },
            "example": "ip,domain"
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "ISO 8601 timestamp.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "min_confidence",
            "in": "query",
            "required": false,
            "description": "Minimum confidence.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum objects.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "STIX bundle",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/Remaining"
              }
            },
            "content": {
              "application/stix+json;version=2.1": {
                "schema": {
                  "type": "object",
                  "description": "STIX 2.1 bundle"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/v1/export": {
      "get": {
        "tags": [
          "Indicators"
        ],
        "operationId": "exportIndicators",
        "summary": "Bulk export, up to 500,000 rows (Business)",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Comma-separated indicator types. Default: every type in your plan (Free: cve,sha256).",
            "schema": {
              "type": "string"
            },
            "example": "ip,domain"
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "ISO 8601 timestamp.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "min_confidence",
            "in": "query",
            "required": false,
            "description": "Minimum confidence.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "Output format.",
            "schema": {
              "type": "string",
              "enum": [
                "csv",
                "json"
              ],
              "default": "csv"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum rows.",
            "schema": {
              "type": "integer",
              "default": 100000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV or JSON",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Indicator"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Create a key under My account."
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer"
      },
      "ApiKeyQuery": {
        "type": "apiKey",
        "in": "query",
        "name": "key",
        "description": "Only for tools that cannot send headers."
      }
    },
    "headers": {
      "Limit": {
        "description": "Daily request quota",
        "schema": {
          "type": "integer"
        }
      },
      "Remaining": {
        "description": "Requests left today (UTC)",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Invalid parameter or feature not included in your plan",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, invalid or revoked API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "QuotaExceeded": {
        "description": "Daily quota reached; resets at midnight UTC",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "Reference": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "source": {
            "type": "string"
          },
          "seen_at": {
            "type": "string"
          },
          "context": {
            "type": "string",
            "description": "Curated feeds only: malware family or campaign label."
          },
          "curated": {
            "type": "boolean",
            "description": "True for curated feeds (ESET, abuse.ch)."
          }
        }
      },
      "Indicator": {
        "type": "object",
        "properties": {
          "value": {
            "type": "string",
            "description": "Domains, URLs and IPs are returned as-is in the API (defanged only on the website)."
          },
          "type": {
            "type": "string",
            "enum": [
              "cve",
              "sha256",
              "sha1",
              "md5",
              "ip",
              "domain",
              "url"
            ]
          },
          "confidence": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Current confidence, after time decay."
          },
          "confidence_base": {
            "type": "integer",
            "description": "Confidence before time decay."
          },
          "severity": {
            "type": [
              "integer",
              "null"
            ]
          },
          "first_seen": {
            "type": "string"
          },
          "last_seen": {
            "type": "string"
          },
          "mentions": {
            "type": "integer"
          },
          "source": {
            "type": "string"
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "malware_family": {
            "type": [
              "string",
              "null"
            ]
          },
          "asn": {
            "type": [
              "integer",
              "null"
            ],
            "description": "IP only."
          },
          "as_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "IP only."
          },
          "country": {
            "type": [
              "string",
              "null"
            ],
            "description": "IP only, ISO 3166-1 alpha-2."
          },
          "references": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Reference"
            },
            "description": "Empty on the Free plan."
          }
        }
      },
      "IndicatorPage": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer"
          },
          "next_until": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `until` to fetch the next page; null on the last page."
          },
          "delayed_hours": {
            "type": "integer"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Indicator"
            }
          }
        }
      },
      "LookupResult": {
        "type": "object",
        "properties": {
          "value": {
            "type": "string"
          },
          "found": {
            "type": "boolean"
          },
          "matches": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Indicator"
            }
          }
        }
      },
      "Stats": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "string"
          },
          "types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "feed": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "sources": {
            "type": "integer"
          },
          "last_sync": {
            "type": "string"
          },
          "quota": {
            "type": "object",
            "properties": {
              "daily": {
                "type": "integer"
              },
              "used_today": {
                "type": "integer"
              }
            }
          }
        }
      },
      "Trend": {
        "type": "object",
        "properties": {
          "value": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "trend_score": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "nouveau",
              "en_hausse",
              "stable",
              "en_baisse"
            ]
          },
          "mentions_7d": {
            "type": "integer"
          },
          "mentions_previous_7d": {
            "type": "integer"
          },
          "active_days_7d": {
            "type": "integer"
          },
          "growth": {
            "type": [
              "number",
              "null"
            ]
          },
          "confidence": {
            "type": "integer"
          },
          "first_seen": {
            "type": "string"
          },
          "last_seen": {
            "type": "string"
          },
          "references": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Reference"
            }
          }
        }
      },
      "TrendingPage": {
        "type": "object",
        "properties": {
          "window": {
            "type": "string"
          },
          "computed_at": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Trend"
            }
          }
        }
      },
      "CveForecast": {
        "type": "object",
        "properties": {
          "cve": {
            "type": "string"
          },
          "signal": {
            "type": "string",
            "enum": [
              "exploitee",
              "imminente",
              "a_surveiller",
              "faible"
            ]
          },
          "signal_label": {
            "type": "string"
          },
          "forecast_score": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Rank-based score among tracked CVEs."
          },
          "kev_probability_30d": {
            "type": "number",
            "description": "Model probability of a CISA KEV addition within 30 days."
          },
          "epss": {
            "type": [
              "number",
              "null"
            ]
          },
          "epss_percentile": {
            "type": [
              "number",
              "null"
            ]
          },
          "epss_date": {
            "type": [
              "string",
              "null"
            ]
          },
          "in_kev": {
            "type": "boolean"
          },
          "kev_date_added": {
            "type": [
              "string",
              "null"
            ]
          },
          "kev_ransomware_use": {
            "type": [
              "string",
              "null"
            ]
          },
          "mentions_7d": {
            "type": "integer"
          },
          "mentions_previous_7d": {
            "type": "integer"
          },
          "active_days_7d": {
            "type": "integer"
          },
          "sources": {
            "type": [
              "integer",
              "null"
            ]
          },
          "first_seen": {
            "type": [
              "string",
              "null"
            ]
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Human-readable explanation (French for now)."
          },
          "computed_at": {
            "type": "string"
          },
          "references": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Reference"
            }
          }
        }
      },
      "ForecastPage": {
        "type": "object",
        "properties": {
          "model": {
            "type": "string"
          },
          "horizon_days": {
            "type": "integer"
          },
          "computed_at": {
            "type": "string"
          },
          "epss_date": {
            "type": "string"
          },
          "kev_version": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CveForecast"
            }
          }
        }
      }
    }
  }
}