diff --git a/README.md b/README.md
index c2dd9953d..5f9c1837d 100644
--- a/README.md
+++ b/README.md
@@ -89,6 +89,7 @@ choco install yasb
| [Battery](https://github.com/amnweb/yasb/wiki/(Widget)-Battery) | Displays the current battery status. |
| [Bluetooth](https://github.com/amnweb/yasb/wiki/(Widget)-Bluetooth) | Shows the current Bluetooth status and connected devices. |
| [Brightness](https://github.com/amnweb/yasb/wiki/(Widget)-Brightness) | Displays and change the current brightness level. |
+| [Burp Suite](https://github.com/amnweb/yasb/wiki/(Widget)-Burp-Suite) | Shows whether Burp Suite is running, its edition, and REST API health. |
| [Cava](https://github.com/amnweb/yasb/wiki/(Widget)-Cava) | Displays audio visualizer using Cava. |
| [Copilot](https://github.com/amnweb/yasb/wiki/(Widget)-Copilot) | GitHub Copilot usage with a detailed menu showing statistics |
| [CPU](https://github.com/amnweb/yasb/wiki/(Widget)-CPU) | Shows the current CPU usage and information. |
diff --git a/docs/_Sidebar.md b/docs/_Sidebar.md
index 602c54fb7..918bba1a5 100644
--- a/docs/_Sidebar.md
+++ b/docs/_Sidebar.md
@@ -15,6 +15,7 @@
- [Battery](./(Widget)-Battery)
- [Bluetooth](./(Widget)-Bluetooth)
- [Brightness](./(Widget)-Brightness)
+ - [Burp Suite](./(Widget)-Burp-Suite)
- [Cava](./(Widget)-Cava)
- [Claude Usage](./(Widget)-Claude-Usage)
- [Copilot](./(Widget)-Copilot)
diff --git a/docs/widgets/(Widget)-Burp-Suite.md b/docs/widgets/(Widget)-Burp-Suite.md
new file mode 100644
index 000000000..e93c9375d
--- /dev/null
+++ b/docs/widgets/(Widget)-Burp-Suite.md
@@ -0,0 +1,130 @@
+# Burp Suite Widget Options
+
+Shows whether [Burp Suite](https://portswigger.net/burp) is running on the bar, the
+detected edition (Professional / Community / Enterprise / DAST), and — when the Burp REST
+API is enabled — whether that service is reachable. Useful as an at-a-glance reminder that
+your proxy is up before you start browsing a target.
+
+The widget is read-only and requires no configuration in Burp for basic presence detection:
+Burp Suite is detected by its window title. The optional REST-API health check probes the
+service root (`GET http://host:port/`), which returns `{"burp_status": "ready"}` and needs
+**no API key**, so no secrets are stored or sent.
+
+| Option | Type | Default | Description |
+|---------------------|---------|---------|-------------|
+| `label` | string | `'{icon} {status}'` | The format string for the label. Supports the placeholders below. |
+| `label_alt` | string | `'{icon} Burp {edition}'` | The alternative format string, toggled by the `toggle_label` callback. |
+| `update_interval` | integer | `5` | How often the status is refreshed, in seconds (1–3600). |
+| `rest_api` | dict | `{enabled: true, host: '127.0.0.1', port: 1337}` | Burp REST API health-check settings. Set `enabled: false` to detect the process only. |
+| `icons` | dict | See below | Icons for each state. |
+| `status_text` | dict | See below | Text shown for each state via `{status}`. |
+| `hide_when_offline` | boolean | `false` | Hide the widget entirely when Burp Suite is not running. |
+| `tooltip` | boolean | `true` | Whether to show a status tooltip on hover. |
+| `callbacks` | dict | `{'on_left': 'toggle_label', 'on_middle': 'do_nothing', 'on_right': 'do_nothing'}` | Mouse-click callbacks. |
+
+## States
+
+The widget reports one of three states, each with its own icon, status text, and CSS class:
+
+- **offline** — no Burp Suite window detected.
+- **running** — Burp Suite is running (REST API disabled, unreachable, or not yet ready).
+- **ready** — Burp Suite is running and its REST API responds with `ready`.
+
+## Placeholders
+
+You can use the following placeholders in `label` / `label_alt`:
+
+- `{icon}` — the state icon (from `icons`); wrap it in a `` to style it as an icon.
+- `{status}` — the state text (from `status_text`).
+- `{edition}` — detected edition: `Pro`, `Community`, `Enterprise`, `DAST`, or `Suite`
+ (empty when offline).
+
+## Example Configuration
+
+```yaml
+burp_suite:
+ type: "yasb.burp_suite.BurpSuiteWidget"
+ options:
+ label: "{icon} {status}"
+ label_alt: "{icon} Burp {edition}"
+ update_interval: 5
+ rest_api:
+ enabled: true
+ host: "127.0.0.1"
+ port: 1337
+ icons:
+ offline: "" # nf-md-shield_off_outline
+ running: "" # nf-md-shield_outline
+ ready: "" # nf-md-shield_check
+ status_text:
+ offline: "Offline"
+ running: "Running"
+ ready: "REST Ready"
+ hide_when_offline: false
+ tooltip: true
+ callbacks:
+ on_left: "toggle_label" # switch between status and edition
+ on_middle: "refresh" # force an immediate re-check
+ on_right: "do_nothing"
+```
+
+## Description of Options
+
+- **label:** The format string for the label. Supports the placeholders listed above.
+- **label_alt:** The alternative format string, toggled with the `toggle_label` callback.
+- **update_interval:** How often the status is refreshed, in seconds (1–3600).
+- **rest_api:** REST API health-check settings:
+ - **enabled:** Whether to probe the REST API. When `false`, the widget only distinguishes
+ `offline` vs `running`.
+ - **host / port:** Where Burp's REST API service is bound (Settings → Suite → REST API).
+- **icons:** Icons shown for each state via `{icon}`.
+- **status_text:** Text shown for each state via `{status}`.
+- **hide_when_offline:** Hide the widget when Burp Suite is not running.
+- **tooltip:** Whether to show a status tooltip on hover.
+- **callbacks:** Mouse-click callbacks. Built-in actions: `toggle_label` (swap between `label`
+ and `label_alt`), `refresh` (force an immediate re-check), `do_nothing`, and `exec`.
+
+## Enabling the Burp REST API (optional)
+
+Presence detection works without this. To light up the `ready` state:
+
+1. In Burp Suite, go to **Settings → Suite → REST API**.
+2. Check **Service running** and note the port (default `1337`).
+
+An API key is not required for the health check this widget performs.
+
+## Widget Style
+```css
+.burp-suite-widget {}
+.burp-suite-widget .widget-container {}
+.burp-suite-widget .icon {}
+.burp-suite-widget .label {}
+/* Per-state classes are applied to both the icon and the label */
+.burp-suite-widget .icon.offline {}
+.burp-suite-widget .icon.running {}
+.burp-suite-widget .icon.ready {}
+.burp-suite-widget .label.offline {}
+.burp-suite-widget .label.running {}
+.burp-suite-widget .label.ready {}
+```
+
+## Example Style
+```css
+.burp-suite-widget {
+ padding: 0 8px;
+}
+.burp-suite-widget .icon {
+ font-size: 14px;
+ margin-right: 4px;
+}
+.burp-suite-widget .label {
+ font-size: 13px;
+ color: #cdd6f4;
+}
+.burp-suite-widget .icon.offline,
+.burp-suite-widget .label.offline { color: #6c7086; }
+.burp-suite-widget .icon.running,
+.burp-suite-widget .label.running { color: #fe640b; } /* Burp orange */
+.burp-suite-widget .icon.ready,
+.burp-suite-widget .label.ready { color: #a6e3a1; }
+```
diff --git a/src/core/validation/widgets/yasb/burp_suite.py b/src/core/validation/widgets/yasb/burp_suite.py
new file mode 100644
index 000000000..dbe3bb72c
--- /dev/null
+++ b/src/core/validation/widgets/yasb/burp_suite.py
@@ -0,0 +1,43 @@
+from pydantic import Field
+
+from core.validation.widgets.base_model import (
+ CallbacksConfig,
+ CustomBaseModel,
+ KeybindingConfig,
+)
+
+
+class BurpSuiteIconsConfig(CustomBaseModel):
+ offline: str = "\U000f099c" # nf-md-shield_off_outline
+ running: str = "\U000f0499" # nf-md-shield_outline
+ ready: str = "\U000f0565" # nf-md-shield_check
+
+
+class BurpSuiteStatusTextConfig(CustomBaseModel):
+ offline: str = "Offline"
+ running: str = "Running"
+ ready: str = "REST Ready"
+
+
+class BurpSuiteRestApiConfig(CustomBaseModel):
+ enabled: bool = True
+ host: str = "127.0.0.1"
+ port: int = Field(default=1337, ge=1, le=65535)
+
+
+class BurpSuiteCallbacksConfig(CallbacksConfig):
+ on_left: str = "toggle_label"
+ on_right: str = "do_nothing"
+
+
+class BurpSuiteConfig(CustomBaseModel):
+ label: str = "{icon} {status}"
+ label_alt: str = "{icon} Burp {edition}"
+ update_interval: int = Field(default=5, ge=1, le=3600)
+ rest_api: BurpSuiteRestApiConfig = BurpSuiteRestApiConfig()
+ icons: BurpSuiteIconsConfig = BurpSuiteIconsConfig()
+ status_text: BurpSuiteStatusTextConfig = BurpSuiteStatusTextConfig()
+ hide_when_offline: bool = False
+ tooltip: bool = True
+ callbacks: BurpSuiteCallbacksConfig = BurpSuiteCallbacksConfig()
+ keybindings: list[KeybindingConfig] = []
diff --git a/src/core/widgets/services/burp_suite/__init__.py b/src/core/widgets/services/burp_suite/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/core/widgets/services/burp_suite/burp_client.py b/src/core/widgets/services/burp_suite/burp_client.py
new file mode 100644
index 000000000..1b9f5d706
--- /dev/null
+++ b/src/core/widgets/services/burp_suite/burp_client.py
@@ -0,0 +1,95 @@
+"""Burp Suite detection and REST API health probing for the Burp Suite widget.
+
+Burp Suite is detected by enumerating top-level window titles (it always sets a
+``Burp Suite ...`` title), which also reveals the edition. When the REST API is
+enabled, the service URL root (``GET http://host:port/``) is probed: it responds
+with ``{"burp_status": "ready"}`` and requires no API key, making it a safe,
+read-only health check. All work runs off the UI thread in a QThread.
+"""
+
+import json
+import logging
+import urllib.request
+
+import win32gui
+from PyQt6.QtCore import QThread, pyqtSignal
+
+logger = logging.getLogger("burp_suite")
+
+# Window-title fragments mapped to a short edition label, checked in order.
+_EDITIONS: list[tuple[str, str]] = [
+ ("professional", "Pro"),
+ ("community", "Community"),
+ ("enterprise", "Enterprise"),
+ ("dast", "DAST"),
+]
+
+# Returned states, ordered by increasing "liveness".
+STATE_OFFLINE = "offline"
+STATE_RUNNING = "running"
+STATE_READY = "ready"
+
+
+def _find_burp_window_title() -> str | None:
+ """Return the title of the first visible Burp Suite window, or None."""
+ titles: list[str] = []
+
+ def _collect(hwnd: int, _lparam: int) -> bool:
+ if win32gui.IsWindowVisible(hwnd):
+ title = win32gui.GetWindowText(hwnd)
+ if title and "burp suite" in title.lower():
+ titles.append(title)
+ return True
+
+ try:
+ win32gui.EnumWindows(_collect, 0)
+ except Exception as e:
+ logger.debug("window enumeration failed: %s", e)
+ return titles[0] if titles else None
+
+
+def _edition_from_title(title: str) -> str:
+ lowered = title.lower()
+ for fragment, label in _EDITIONS:
+ if fragment in lowered:
+ return label
+ return "Suite"
+
+
+def _rest_api_ready(host: str, port: int) -> bool:
+ """Probe the REST API service root. True only when Burp reports ``ready``."""
+ url = f"http://{host}:{port}/"
+ try:
+ with urllib.request.urlopen(url, timeout=2) as response:
+ payload = json.loads(response.read().decode("utf-8"))
+ return payload.get("burp_status") == "ready"
+ except Exception as e:
+ logger.debug("REST API probe failed: %s", e)
+ return False
+
+
+def probe_burp(rest_enabled: bool, host: str, port: int) -> dict[str, object]:
+ """Build a status record describing the current Burp Suite state."""
+ title = _find_burp_window_title()
+ if title is None:
+ return {"state": STATE_OFFLINE, "edition": "", "rest_ready": False}
+
+ edition = _edition_from_title(title)
+ rest_ready = rest_enabled and _rest_api_ready(host, port)
+ state = STATE_READY if rest_ready else STATE_RUNNING
+ return {"state": state, "edition": edition, "rest_ready": rest_ready}
+
+
+class BurpStatusWorker(QThread):
+ """Runs window enumeration + the (blocking) REST probe off the UI thread."""
+
+ status_ready = pyqtSignal(dict)
+
+ def __init__(self, rest_enabled: bool, host: str, port: int, parent: object = None):
+ super().__init__(parent)
+ self._rest_enabled = rest_enabled
+ self._host = host
+ self._port = port
+
+ def run(self) -> None:
+ self.status_ready.emit(probe_burp(self._rest_enabled, self._host, self._port))
diff --git a/src/core/widgets/yasb/burp_suite.py b/src/core/widgets/yasb/burp_suite.py
new file mode 100644
index 000000000..b458a38fa
--- /dev/null
+++ b/src/core/widgets/yasb/burp_suite.py
@@ -0,0 +1,128 @@
+import re
+from typing import Any
+
+from core.utils.tooltip import set_tooltip
+from core.utils.utilities import refresh_widget_style
+from core.validation.widgets.yasb.burp_suite import BurpSuiteConfig
+from core.widgets.base import BaseWidget
+from core.widgets.services.burp_suite.burp_client import (
+ STATE_OFFLINE,
+ BurpStatusWorker,
+)
+
+
+class BurpSuiteWidget(BaseWidget):
+ validation_schema = BurpSuiteConfig
+
+ def __init__(self, config: BurpSuiteConfig):
+ super().__init__(class_name="burp-suite-widget")
+ self.config = config
+ self._show_alt_label = False
+ self._worker: BurpStatusWorker | None = None
+ self._data: dict[str, Any] = {"state": STATE_OFFLINE, "edition": "", "rest_ready": False}
+
+ self._icons = config.icons.model_dump()
+ self._status_text = config.status_text.model_dump()
+
+ self._init_container()
+ self.build_widget_label(self.config.label, self.config.label_alt)
+
+ self.register_callback("toggle_label", self._toggle_label)
+ self.register_callback("refresh", self._refresh)
+
+ self.callback_left = self.config.callbacks.on_left
+ self.callback_middle = self.config.callbacks.on_middle
+ self.callback_right = self.config.callbacks.on_right
+ self.callback_timer = "refresh"
+
+ self.timer.setInterval(self.config.update_interval * 1000)
+ self.timer.timeout.connect(self._tick)
+ self.timer.start()
+
+ self.destroyed.connect(lambda *_: self._stop_worker())
+ self._tick()
+ self._update_label()
+
+ def _stop_worker(self) -> None:
+ worker = self._worker
+ if worker is not None and worker.isRunning():
+ worker.wait(2000)
+
+ def _tick(self) -> None:
+ if self._worker is not None and self._worker.isRunning():
+ return # a probe is already in flight
+ worker = BurpStatusWorker(
+ self.config.rest_api.enabled,
+ self.config.rest_api.host,
+ self.config.rest_api.port,
+ self,
+ )
+ worker.status_ready.connect(self._on_status)
+ worker.finished.connect(self._on_worker_finished)
+ self._worker = worker
+ worker.start()
+
+ def _on_worker_finished(self) -> None:
+ worker = self._worker
+ self._worker = None
+ if worker is not None:
+ worker.deleteLater()
+
+ def _refresh(self) -> None:
+ self._tick()
+
+ def _on_status(self, data: dict[str, Any]) -> None:
+ self._data = data
+ self._update_label()
+
+ def _format_values(self) -> dict[str, str]:
+ state = str(self._data.get("state", STATE_OFFLINE))
+ return {
+ "icon": self._icons.get(state, ""),
+ "status": self._status_text.get(state, ""),
+ "edition": str(self._data.get("edition", "")),
+ }
+
+ def _toggle_label(self) -> None:
+ self._show_alt_label = not self._show_alt_label
+ for widget in self._widgets:
+ widget.setVisible(not self._show_alt_label)
+ for widget in self._widgets_alt:
+ widget.setVisible(self._show_alt_label)
+ self._update_label()
+
+ def _update_label(self) -> None:
+ state = str(self._data.get("state", STATE_OFFLINE))
+
+ if self.config.hide_when_offline:
+ self._widget_frame.setVisible(state != STATE_OFFLINE)
+
+ active_widgets = self._widgets_alt if self._show_alt_label else self._widgets
+ active_template = self.config.label_alt if self._show_alt_label else self.config.label
+ values = self._format_values()
+ # Drop whitespace-only parts so widget/template indices stay aligned with
+ # build_widget_label (which discards them); otherwise multi-span labels misalign.
+ label_parts = [part for part in re.split(r"(.*?)", active_template) if part.strip()]
+
+ for index, part in enumerate(label_parts):
+ if index >= len(active_widgets):
+ continue
+ current_widget = active_widgets[index]
+ is_icon = "" in part
+ base_class = "icon" if is_icon else "label"
+ current_widget.setProperty("class", f"{base_class} {state}")
+ if is_icon:
+ text = re.sub(r"|", "", part).strip()
+ else:
+ text = part.strip()
+ try:
+ current_widget.setText(text.format(**values))
+ except Exception:
+ current_widget.setText(text)
+ if self.config.tooltip:
+ edition = values["edition"]
+ summary = f"Burp Suite — {values['status']}"
+ if edition:
+ summary += f" ({edition})"
+ set_tooltip(current_widget, summary)
+ refresh_widget_style(*active_widgets)