diff --git a/src/Utils.cpp b/src/Utils.cpp
index 5bd2823d..ae8a2aa0 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -22,6 +22,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
 
 #include <obs-frontend-api.h>
 #include <obs.hpp>
+#include <util/platform.h>
+
 #include "obs-websocket.h"
 
 #include "Utils.h"
@@ -764,3 +766,51 @@ obs_data_array_t* Utils::GetSourceFiltersList(obs_source_t* source, bool include
 
 	return enumParams.filters;
 }
+
+void getPauseRecordingFunctions(RecordingPausedFunction* recPausedFuncPtr, PauseRecordingFunction* pauseRecFuncPtr)
+{
+	void* frontendApi = os_dlopen("obs-frontend-api");
+
+	if (recPausedFuncPtr) {
+		*recPausedFuncPtr = (RecordingPausedFunction)os_dlsym(frontendApi, "obs_frontend_recording_paused");
+	}
+
+	if (pauseRecFuncPtr) {
+		*pauseRecFuncPtr = (PauseRecordingFunction)os_dlsym(frontendApi, "obs_frontend_recording_pause");
+	}
+
+	os_dlclose(frontendApi);
+}
+
+bool Utils::RecordingPauseSupported()
+{
+	RecordingPausedFunction recordingPaused = nullptr;
+	PauseRecordingFunction pauseRecording = nullptr;
+	getPauseRecordingFunctions(&recordingPaused, &pauseRecording);
+
+	return (recordingPaused && pauseRecording);
+}
+
+bool Utils::RecordingPaused()
+{
+	RecordingPausedFunction recordingPaused = nullptr;
+	getPauseRecordingFunctions(&recordingPaused, nullptr);
+
+	if (recordingPaused == nullptr) {
+		return false;
+	}
+
+	return recordingPaused();
+}
+
+void Utils::PauseRecording(bool pause)
+{
+	PauseRecordingFunction pauseRecording = nullptr;
+	getPauseRecordingFunctions(nullptr, &pauseRecording);
+
+	if (pauseRecording == nullptr) {
+		return;
+	}
+
+	pauseRecording(pause); 
+}
diff --git a/src/Utils.h b/src/Utils.h
index 6a446a7c..6de57eec 100644
--- a/src/Utils.h
+++ b/src/Utils.h
@@ -31,6 +31,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
 #include <obs-module.h>
 #include <util/config-file.h>
 
+typedef void(*PauseRecordingFunction)(bool);
+typedef bool(*RecordingPausedFunction)();
+
 class Utils {
   public:
 	static obs_data_array_t* StringListToArray(char** strings, const char* key);
@@ -78,4 +81,8 @@ class Utils {
 
 	static const char* GetFilenameFormatting();
 	static bool SetFilenameFormatting(const char* filenameFormatting);
+
+	static bool RecordingPauseSupported();
+	static bool RecordingPaused();
+	static void PauseRecording(bool pause);
 };
diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp
index a5525925..a1979492 100644
--- a/src/WSEvents.cpp
+++ b/src/WSEvents.cpp
@@ -17,7 +17,9 @@
  * with this program. If not, see <https://www.gnu.org/licenses/>
  */
 
+#include <inttypes.h>
 #include <util/platform.h>
+#include <media-io/video-io.h>
 
 #include <QtWidgets/QPushButton>
 
@@ -29,7 +31,7 @@
 
 #define STATUS_INTERVAL 2000
 
-const char* nsToTimestamp(uint64_t ns) {
+QString nsToTimestamp(uint64_t ns) {
 	uint64_t ms = ns / 1000000ULL;
 	uint64_t secs = ms / 1000ULL;
 	uint64_t minutes = secs / 60ULL;
@@ -39,10 +41,7 @@ const char* nsToTimestamp(uint64_t ns) {
 	uint64_t secsPart = secs % 60ULL;
 	uint64_t msPart = ms % 1000ULL;
 
-	char* ts = (char*)bmalloc(64);
-	sprintf(ts, "%02llu:%02llu:%02llu.%03llu", hoursPart, minutesPart, secsPart, msPart);
-
-	return ts;
+	return QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
 }
 
 const char* sourceTypeToString(obs_source_type type) {
@@ -75,7 +74,8 @@ const char* calldata_get_string(const calldata_t* data, const char* name) {
 WSEvents::WSEvents(WSServerPtr srv) :
 	_srv(srv),
 	_streamStarttime(0),
-	_recStarttime(0),
+	_lastBytesSent(0),
+	_lastBytesSentTime(0),
 	HeartbeatIsActive(false),
 	pulse(false)
 {
@@ -206,6 +206,14 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private
 			owner->OnRecordingStopped();
 			break;
 
+		case OBS_FRONTEND_EVENT_RECORDING_PAUSED:
+			owner->OnRecordingPaused();
+			break;
+
+		case OBS_FRONTEND_EVENT_RECORDING_UNPAUSED:
+			owner->OnRecordingResumed();
+			break;
+
 		case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING:
 			owner->OnReplayStarting();
 			break;
@@ -247,17 +255,14 @@ void WSEvents::broadcastUpdate(const char* updateType,
 	OBSDataAutoRelease update = obs_data_create();
 	obs_data_set_string(update, "update-type", updateType);
 
-	const char* ts = nullptr;
 	if (obs_frontend_streaming_active()) {
-		ts = nsToTimestamp(os_gettime_ns() - _streamStarttime);
-		obs_data_set_string(update, "stream-timecode", ts);
-		bfree((void*)ts);
+		QString streamingTimecode = getStreamingTimecode();
+		obs_data_set_string(update, "stream-timecode", streamingTimecode.toUtf8().constData());
 	}
 
 	if (obs_frontend_recording_active()) {
-		ts = nsToTimestamp(os_gettime_ns() - _recStarttime);
-		obs_data_set_string(update, "rec-timecode", ts);
-		bfree((void*)ts);
+		QString recordingTimecode = getRecordingTimecode();
+		obs_data_set_string(update, "rec-timecode", recordingTimecode.toUtf8().constData());
 	}
 
 	if (additionalFields)
@@ -362,26 +367,34 @@ void WSEvents::unhookTransitionBeginEvent() {
 	obs_frontend_source_list_free(&transitions);
 }
 
-uint64_t WSEvents::GetStreamingTime() {
-	if (obs_frontend_streaming_active())
-		return (os_gettime_ns() - _streamStarttime);
-	else
+uint64_t getOutputRunningTime(obs_output_t* output) {
+	if (!output || !obs_output_active(output)) {
 		return 0;
+	}
+
+	video_t* video = obs_output_video(output);
+	uint64_t frameTimeNs = video_output_get_frame_time(video);
+	int totalFrames = obs_output_get_total_frames(output);
+
+	return (((uint64_t)totalFrames) * frameTimeNs);
 }
 
-const char* WSEvents::GetStreamingTimecode() {
-	return nsToTimestamp(GetStreamingTime());
+uint64_t WSEvents::getStreamingTime() {
+	OBSOutputAutoRelease streamingOutput = obs_frontend_get_streaming_output();
+	return getOutputRunningTime(streamingOutput);
 }
 
-uint64_t WSEvents::GetRecordingTime() {
-	if (obs_frontend_recording_active())
-		return (os_gettime_ns() - _recStarttime);
-	else
-		return 0;
+uint64_t WSEvents::getRecordingTime() {
+	OBSOutputAutoRelease recordingOutput = obs_frontend_get_recording_output();
+	return getOutputRunningTime(recordingOutput);
 }
 
-const char* WSEvents::GetRecordingTimecode() {
-	return nsToTimestamp(GetRecordingTime());
+QString WSEvents::getStreamingTimecode() {
+	return nsToTimestamp(getStreamingTime());
+}
+
+QString WSEvents::getRecordingTimecode() {
+	return nsToTimestamp(getRecordingTime());
 }
 
  /**
@@ -534,6 +547,7 @@ void WSEvents::OnStreamStarting() {
 void WSEvents::OnStreamStarted() {
 	_streamStarttime = os_gettime_ns();
 	_lastBytesSent = 0;
+
 	broadcastUpdate("StreamStarted");
 }
 
@@ -550,7 +564,6 @@ void WSEvents::OnStreamStarted() {
 void WSEvents::OnStreamStopping() {
 	OBSDataAutoRelease data = obs_data_create();
 	obs_data_set_bool(data, "preview-only", false);
-
 	broadcastUpdate("StreamStopping", data);
 }
 
@@ -564,6 +577,7 @@ void WSEvents::OnStreamStopping() {
  */
 void WSEvents::OnStreamStopped() {
 	_streamStarttime = 0;
+
 	broadcastUpdate("StreamStopped");
 }
 
@@ -588,7 +602,6 @@ void WSEvents::OnRecordingStarting() {
  * @since 0.3
  */
 void WSEvents::OnRecordingStarted() {
-	_recStarttime = os_gettime_ns();
 	broadcastUpdate("RecordingStarted");
 }
 
@@ -613,10 +626,33 @@ void WSEvents::OnRecordingStopping() {
  * @since 0.3
  */
 void WSEvents::OnRecordingStopped() {
-	_recStarttime = 0;
 	broadcastUpdate("RecordingStopped");
 }
 
+/**
+ * Current recording paused
+ *
+ * @api events
+ * @name RecordingPaused
+ * @category recording
+ * @since 4.7.0
+ */
+void WSEvents::OnRecordingPaused() {
+	broadcastUpdate("RecordingPaused");
+}
+
+/**
+ * Current recording resumed
+ *
+ * @api events
+ * @name RecordingResumed
+ * @category recording
+ * @since 4.7.0
+ */
+void WSEvents::OnRecordingResumed() {
+	broadcastUpdate("RecordingResumed");
+}
+
 /**
 * A request to start the replay buffer has been issued.
 *
@@ -708,6 +744,7 @@ void WSEvents::OnExit() {
 void WSEvents::StreamStatus() {
 	bool streamingActive = obs_frontend_streaming_active();
 	bool recordingActive = obs_frontend_recording_active();
+	bool recordingPaused = Utils::RecordingPaused();
 	bool replayBufferActive = obs_frontend_replay_buffer_active();
 
 	OBSOutputAutoRelease streamOutput = obs_frontend_get_streaming_output();
@@ -734,9 +771,7 @@ void WSEvents::StreamStatus() {
 	_lastBytesSent = bytesSent;
 	_lastBytesSentTime = bytesSentTime;
 
-	uint64_t totalStreamTime =
-		(os_gettime_ns() - _streamStarttime) / 1000000000;
-
+	uint64_t totalStreamTime = (getStreamingTime() / 1000000000ULL);
 	int totalFrames = obs_output_get_total_frames(streamOutput);
 	int droppedFrames = obs_output_get_frames_dropped(streamOutput);
 
@@ -746,6 +781,7 @@ void WSEvents::StreamStatus() {
 
 	obs_data_set_bool(data, "streaming", streamingActive);
 	obs_data_set_bool(data, "recording", recordingActive);
+	obs_data_set_bool(data, "recording-paused", recordingPaused);
 	obs_data_set_bool(data, "replay-buffer-active", replayBufferActive);
 
 	obs_data_set_int(data, "bytes-per-sec", bytesPerSec);
@@ -792,6 +828,7 @@ void WSEvents::Heartbeat() {
 
 	bool streamingActive = obs_frontend_streaming_active();
 	bool recordingActive = obs_frontend_recording_active();
+	bool recordingPaused = Utils::RecordingPaused();
 
 	OBSDataAutoRelease data = obs_data_create();
 	OBSOutputAutoRelease recordOutput = obs_frontend_get_recording_output();
@@ -807,16 +844,15 @@ void WSEvents::Heartbeat() {
 
 	obs_data_set_bool(data, "streaming", streamingActive);
 	if (streamingActive) {
-		uint64_t totalStreamTime = (os_gettime_ns() - _streamStarttime) / 1000000000;
-		obs_data_set_int(data, "total-stream-time", totalStreamTime);
+		obs_data_set_int(data, "total-stream-time", (getStreamingTime() / 1000000000ULL));
 		obs_data_set_int(data, "total-stream-bytes", (uint64_t)obs_output_get_total_bytes(streamOutput));
 		obs_data_set_int(data, "total-stream-frames", obs_output_get_total_frames(streamOutput));
 	}
 
 	obs_data_set_bool(data, "recording", recordingActive);
+	obs_data_set_bool(data, "recording-paused", recordingPaused);
 	if (recordingActive) {
-		uint64_t totalRecordTime = (os_gettime_ns() - _recStarttime) / 1000000000;
-		obs_data_set_int(data, "total-record-time", totalRecordTime);
+		obs_data_set_int(data, "total-record-time", (getRecordingTime() / 1000000000ULL));
 		obs_data_set_int(data, "total-record-bytes", (uint64_t)obs_output_get_total_bytes(recordOutput));
 		obs_data_set_int(data, "total-record-frames", obs_output_get_total_frames(recordOutput));
 	}
diff --git a/src/WSEvents.h b/src/WSEvents.h
index af6e2da3..85be443f 100644
--- a/src/WSEvents.h
+++ b/src/WSEvents.h
@@ -43,10 +43,12 @@ public:
 	void hookTransitionBeginEvent();
 	void unhookTransitionBeginEvent();
 
-	uint64_t GetStreamingTime();
-	const char* GetStreamingTimecode();
-	uint64_t GetRecordingTime();
-	const char* GetRecordingTimecode();
+	uint64_t getStreamingTime();
+	uint64_t getRecordingTime();
+
+	QString getStreamingTimecode();
+	QString getRecordingTimecode();
+	
 	obs_data_t* GetStats();
 
 	void OnBroadcastCustomMessage(QString realm, obs_data_t* data);
@@ -67,7 +69,6 @@ private:
 	bool pulse;
 
 	uint64_t _streamStarttime;
-	uint64_t _recStarttime;
 
 	uint64_t _lastBytesSent;
 	uint64_t _lastBytesSentTime;
@@ -95,6 +96,8 @@ private:
 	void OnRecordingStarted();
 	void OnRecordingStopping();
 	void OnRecordingStopped();
+	void OnRecordingPaused();
+	void OnRecordingResumed();
 
 	void OnReplayStarting();
 	void OnReplayStarted();
diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp
index dabf30bc..2ff897fe 100644
--- a/src/WSRequestHandler.cpp
+++ b/src/WSRequestHandler.cpp
@@ -24,7 +24,7 @@
 
 #include "WSRequestHandler.h"
 
-QHash<QString, HandlerResponse(*)(WSRequestHandler*)> WSRequestHandler::messageMap {
+QHash<QString, HandlerResponse(*)(WSRequestHandler*)> WSRequestHandler::messageMap{
 	{ "GetVersion", WSRequestHandler::HandleGetVersion },
 	{ "GetAuthRequired", WSRequestHandler::HandleGetAuthRequired },
 	{ "Authenticate", WSRequestHandler::HandleAuthenticate },
@@ -57,10 +57,14 @@ QHash<QString, HandlerResponse(*)(WSRequestHandler*)> WSRequestHandler::messageM
 	{ "GetStreamingStatus", WSRequestHandler::HandleGetStreamingStatus },
 	{ "StartStopStreaming", WSRequestHandler::HandleStartStopStreaming },
 	{ "StartStopRecording", WSRequestHandler::HandleStartStopRecording },
+
 	{ "StartStreaming", WSRequestHandler::HandleStartStreaming },
 	{ "StopStreaming", WSRequestHandler::HandleStopStreaming },
+
 	{ "StartRecording", WSRequestHandler::HandleStartRecording },
 	{ "StopRecording", WSRequestHandler::HandleStopRecording },
+	{ "PauseRecording", WSRequestHandler::HandlePauseRecording },
+	{ "ResumeRecording", WSRequestHandler::HandleResumeRecording },
 
 	{ "StartStopReplayBuffer", WSRequestHandler::HandleStartStopReplayBuffer },
 	{ "StartReplayBuffer", WSRequestHandler::HandleStartReplayBuffer },
diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h
index d9265ebf..bee9566b 100644
--- a/src/WSRequestHandler.h
+++ b/src/WSRequestHandler.h
@@ -103,10 +103,14 @@ class WSRequestHandler : public QObject {
 		static HandlerResponse HandleGetStreamingStatus(WSRequestHandler* req);
 		static HandlerResponse HandleStartStopStreaming(WSRequestHandler* req);
 		static HandlerResponse HandleStartStopRecording(WSRequestHandler* req);
+
 		static HandlerResponse HandleStartStreaming(WSRequestHandler* req);
 		static HandlerResponse HandleStopStreaming(WSRequestHandler* req);
+
 		static HandlerResponse HandleStartRecording(WSRequestHandler* req);
 		static HandlerResponse HandleStopRecording(WSRequestHandler* req);
+		static HandlerResponse HandlePauseRecording(WSRequestHandler* req);
+		static HandlerResponse HandleResumeRecording(WSRequestHandler* req);
 
 		static HandlerResponse HandleStartStopReplayBuffer(WSRequestHandler* req);
 		static HandlerResponse HandleStartReplayBuffer(WSRequestHandler* req);
diff --git a/src/WSRequestHandler_Recording.cpp b/src/WSRequestHandler_Recording.cpp
index b8600935..9164a404 100644
--- a/src/WSRequestHandler_Recording.cpp
+++ b/src/WSRequestHandler_Recording.cpp
@@ -1,6 +1,20 @@
+#include "WSRequestHandler.h"
+
+#include <util/platform.h>
 #include "Utils.h"
 
-#include "WSRequestHandler.h"
+HandlerResponse ifCanPause(WSRequestHandler* req, std::function<HandlerResponse()> callback)
+{
+	if (!obs_frontend_recording_active()) {
+		return req->SendErrorResponse("recording is not active");
+	}
+
+	if (!Utils::RecordingPauseSupported()) {
+		return req->SendErrorResponse("recording pauses are not available in this version of OBS Studio");
+	}
+
+	return callback();
+}
 
 /**
  * Toggle recording on or off.
@@ -11,11 +25,7 @@
  * @since 0.3
  */
 HandlerResponse WSRequestHandler::HandleStartStopRecording(WSRequestHandler* req) {
-	if (obs_frontend_recording_active())
-		obs_frontend_recording_stop();
-	else
-		obs_frontend_recording_start();
-
+	(obs_frontend_recording_active() ? obs_frontend_recording_stop() : obs_frontend_recording_start());
 	return req->SendOKResponse();
 }
 
@@ -29,12 +39,12 @@ HandlerResponse WSRequestHandler::HandleStartStopRecording(WSRequestHandler* req
  * @since 4.1.0
  */
 HandlerResponse WSRequestHandler::HandleStartRecording(WSRequestHandler* req) {
-	if (obs_frontend_recording_active() == false) {
-		obs_frontend_recording_start();
-		return req->SendOKResponse();
-	} else {
+	if (obs_frontend_recording_active()) {
 		return req->SendErrorResponse("recording already active");
 	}
+
+	obs_frontend_recording_start();
+	return req->SendOKResponse();
 }
 
 /**
@@ -47,12 +57,52 @@ HandlerResponse WSRequestHandler::HandleStartRecording(WSRequestHandler* req) {
  * @since 4.1.0
  */
  HandlerResponse WSRequestHandler::HandleStopRecording(WSRequestHandler* req) {
-	if (obs_frontend_recording_active() == true) {
-		obs_frontend_recording_stop();
-		return req->SendOKResponse();
-	} else {
+	if (!obs_frontend_recording_active()) {
 		return req->SendErrorResponse("recording not active");
 	}
+
+	obs_frontend_recording_stop();
+	return req->SendOKResponse();
+}
+
+/**
+* Pause the current recording.
+* Returns an error if recording is not active or already paused.
+*
+* @api requests
+* @name PauseRecording
+* @category recording
+* @since 4.7.0
+*/
+HandlerResponse WSRequestHandler::HandlePauseRecording(WSRequestHandler* req) {
+	return ifCanPause(req, [req]() {
+		if (Utils::RecordingPaused()) {
+			return req->SendErrorResponse("recording already paused");
+		}
+
+		Utils::PauseRecording(true);
+		return req->SendOKResponse();
+	});
+}
+
+/**
+* Resume/unpause the current recording (if paused).
+* Returns an error if recording is not active or not paused.
+*
+* @api requests
+* @name ResumeRecording
+* @category recording
+* @since 4.7.0
+*/
+HandlerResponse WSRequestHandler::HandleResumeRecording(WSRequestHandler* req) {
+	return ifCanPause(req, [req]() {
+		if (!Utils::RecordingPaused()) {
+			return req->SendErrorResponse("recording is not paused");
+		}
+
+		Utils::PauseRecording(false);
+		return req->SendOKResponse();
+	});
 }
 
 /**
@@ -63,7 +113,6 @@ HandlerResponse WSRequestHandler::HandleStartRecording(WSRequestHandler* req) {
  * in progress, the change won't be applied immediately and will be
  * effective on the next recording.
  * 
- * 
  * @param {String} `rec-folder` Path of the recording folder.
  *
  * @api requests
diff --git a/src/WSRequestHandler_Streaming.cpp b/src/WSRequestHandler_Streaming.cpp
index bbbf4bb1..0daea177 100644
--- a/src/WSRequestHandler_Streaming.cpp
+++ b/src/WSRequestHandler_Streaming.cpp
@@ -26,19 +26,17 @@ HandlerResponse WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req
 	OBSDataAutoRelease data = obs_data_create();
 	obs_data_set_bool(data, "streaming", obs_frontend_streaming_active());
 	obs_data_set_bool(data, "recording", obs_frontend_recording_active());
+	obs_data_set_bool(data, "recording-paused", Utils::RecordingPaused());
 	obs_data_set_bool(data, "preview-only", false);
 
-	const char* tc = nullptr;
 	if (obs_frontend_streaming_active()) {
-		tc = events->GetStreamingTimecode();
-		obs_data_set_string(data, "stream-timecode", tc);
-		bfree((void*)tc);
+		QString streamingTimecode = events->getStreamingTimecode();
+		obs_data_set_string(data, "stream-timecode", streamingTimecode.toUtf8().constData());
 	}
 
 	if (obs_frontend_recording_active()) {
-		tc = events->GetRecordingTimecode();
-		obs_data_set_string(data, "rec-timecode", tc);
-		bfree((void*)tc);
+		QString recordingTimecode = events->getRecordingTimecode();
+		obs_data_set_string(data, "rec-timecode", recordingTimecode.toUtf8().constData());
 	}
 
 	return req->SendOKResponse(data);