diff --git a/src/Utils.cpp b/src/Utils.cpp
index c08b5416..5bd2823d 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -173,23 +173,39 @@ obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item) {
 	return data;
 }
 
-obs_sceneitem_t* Utils::GetSceneItemFromItem(obs_source_t* source, obs_data_t* item) {
-        OBSSceneItem sceneItem;
-        if (obs_data_has_user_value(item, "id")) {
-                sceneItem = GetSceneItemFromId(source, obs_data_get_int(item, "id"));
-                if (obs_data_has_user_value(item, "name") &&
-                   (QString)obs_source_get_name(obs_sceneitem_get_source(sceneItem)) !=
-                   (QString)obs_data_get_string(item, "name")) {
-                        return nullptr;
-                }
-        }
-        else if (obs_data_has_user_value(item, "name")) {
-                sceneItem = GetSceneItemFromName(source, obs_data_get_string(item, "name"));
-        }
-        return sceneItem;
+obs_sceneitem_t* Utils::GetSceneItemFromItem(obs_scene_t* scene, obs_data_t* itemInfo) {
+	if (!scene) {
+		return nullptr;
+	}
+
+	OBSDataItemAutoRelease idInfoItem = obs_data_item_byname(itemInfo, "id");
+	int id = obs_data_item_get_int(idInfoItem);
+
+	OBSDataItemAutoRelease nameInfoItem = obs_data_item_byname(itemInfo, "name");
+	const char* name = obs_data_item_get_string(nameInfoItem);
+
+	if (idInfoItem) {
+		obs_sceneitem_t* sceneItem = GetSceneItemFromId(scene, id);
+		obs_source_t* sceneItemSource = obs_sceneitem_get_source(sceneItem);
+
+		QString sceneItemName = obs_source_get_name(sceneItemSource);
+		if (nameInfoItem && (QString(name) != sceneItemName)) {
+			return nullptr;
+		}
+
+		return sceneItem;
+	} else if (nameInfoItem) {
+		return GetSceneItemFromName(scene, name);
+	}
+
+	return nullptr;
 }
 
-obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, QString name) {
+obs_sceneitem_t* Utils::GetSceneItemFromName(obs_scene_t* scene, QString name) {
+	if (!scene) {
+		return nullptr;
+	}
+
 	struct current_search {
 		QString query;
 		obs_sceneitem_t* result;
@@ -199,11 +215,6 @@ obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, QString name)
 	current_search search;
 	search.query = name;
 	search.result = nullptr;
-	search.enumCallback = nullptr;
-
-	OBSScene scene = obs_scene_from_source(source);
-	if (!scene)
-		return nullptr;
 
 	search.enumCallback = [](
 			obs_scene_t* scene,
@@ -236,10 +247,13 @@ obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, QString name)
 	return search.result;
 }
 
-// TODO refactor this to unify it with GetSceneItemFromName
-obs_sceneitem_t* Utils::GetSceneItemFromId(obs_source_t* source, size_t id) {
+obs_sceneitem_t* Utils::GetSceneItemFromId(obs_scene_t* scene, int64_t id) {
+	if (!scene) {
+		return nullptr;
+	}
+
 	struct current_search {
-		size_t query;
+		int query;
 		obs_sceneitem_t* result;
 		bool (*enumCallback)(obs_scene_t*, obs_sceneitem_t*, void*);
 	};
@@ -247,21 +261,16 @@ obs_sceneitem_t* Utils::GetSceneItemFromId(obs_source_t* source, size_t id) {
 	current_search search;
 	search.query = id;
 	search.result = nullptr;
-	search.enumCallback = nullptr;
-
-	OBSScene scene = obs_scene_from_source(source);
-	if (!scene)
-		return nullptr;
 
 	search.enumCallback = [](
-			obs_scene_t* scene,
-			obs_sceneitem_t* currentItem,
-			void* param)
+		obs_scene_t* scene,
+		obs_sceneitem_t* currentItem,
+		void* param)
 	{
 		current_search* search = reinterpret_cast<current_search*>(param);
 
 		if (obs_sceneitem_is_group(currentItem)) {
-			obs_sceneitem_group_enum_items(currentItem, search->enumCallback, param);
+			obs_sceneitem_group_enum_items(currentItem, search->enumCallback, search);
 			if (search->result) {
 				return false;
 			}
@@ -319,17 +328,19 @@ obs_source_t* Utils::GetTransitionFromName(QString searchName) {
 	return foundTransition;
 }
 
-obs_source_t* Utils::GetSceneFromNameOrCurrent(QString sceneName) {
+obs_scene_t* Utils::GetSceneFromNameOrCurrent(QString sceneName) {
 	// Both obs_frontend_get_current_scene() and obs_get_source_by_name()
-	// do addref on the return source, so no need to use an OBSSource helper
-	obs_source_t* scene = nullptr;
+	// increase the returned source's refcount
+	OBSSourceAutoRelease sceneSource = nullptr;
 
-	if (sceneName.isEmpty() || sceneName.isNull())
-		scene = obs_frontend_get_current_scene();
-	else
-		scene = obs_get_source_by_name(sceneName.toUtf8());
+	if (sceneName.isEmpty() || sceneName.isNull()) {
+		sceneSource = obs_frontend_get_current_scene();
+	}
+	else {
+		sceneSource = obs_get_source_by_name(sceneName.toUtf8());
+	}
 
-	return scene;
+	return obs_scene_from_source(sceneSource);
 }
 
 obs_data_array_t* Utils::GetScenes() {
diff --git a/src/Utils.h b/src/Utils.h
index 8e2d410d..6a446a7c 100644
--- a/src/Utils.h
+++ b/src/Utils.h
@@ -36,12 +36,13 @@ class Utils {
 	static obs_data_array_t* StringListToArray(char** strings, const char* key);
 	static obs_data_array_t* GetSceneItems(obs_source_t* source);
 	static obs_data_t* GetSceneItemData(obs_sceneitem_t* item);
-	static obs_sceneitem_t* GetSceneItemFromName(
-		obs_source_t* source, QString name);
-        static obs_sceneitem_t* GetSceneItemFromId(obs_source_t* source, size_t id);
-	static obs_sceneitem_t* GetSceneItemFromItem(obs_source_t* source, obs_data_t* item);
-	static obs_source_t* GetTransitionFromName(QString transitionName);
-	static obs_source_t* GetSceneFromNameOrCurrent(QString sceneName);
+
+	// These two functions support nested lookup into groups
+	static obs_sceneitem_t* GetSceneItemFromName(obs_scene_t* scene, QString name);
+	static obs_sceneitem_t* GetSceneItemFromId(obs_scene_t* scene, int64_t id);
+
+	static obs_sceneitem_t* GetSceneItemFromItem(obs_scene_t* scene, obs_data_t* item);
+	static obs_scene_t* GetSceneFromNameOrCurrent(QString sceneName);
 	static obs_data_t* GetSceneItemPropertiesData(obs_sceneitem_t* item);
 
 	static obs_data_array_t* GetSourceFiltersList(obs_source_t* source, bool includeSettings);
@@ -53,9 +54,8 @@ class Utils {
 
 	// TODO contribute a proper frontend API method for this to OBS and remove this hack
 	static QSpinBox* GetTransitionDurationControl();
-
 	static int GetTransitionDuration(obs_source_t* transition);
-
+	static obs_source_t* GetTransitionFromName(QString transitionName);
 	static bool SetTransitionByName(QString transitionName);
 
 	static QString OBSVersionString();
@@ -71,9 +71,11 @@ class Utils {
 
 	static QString ParseDataToQueryString(obs_data_t* data);
 	static obs_hotkey_t* FindHotkeyByName(QString name);
+
 	static bool ReplayBufferEnabled();
 	static void StartReplayBuffer();
 	static bool IsRPHotkeySet();
+
 	static const char* GetFilenameFormatting();
 	static bool SetFilenameFormatting(const char* filenameFormatting);
 };
diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp
index fd5c138f..e2e6d188 100644
--- a/src/WSRequestHandler_SceneItems.cpp
+++ b/src/WSRequestHandler_SceneItems.cpp
@@ -49,13 +49,12 @@ HandlerResponse WSRequestHandler::HandleGetSceneItemProperties(WSRequestHandler*
 	}
 
 	QString sceneName = obs_data_get_string(req->data, "scene-name");
-	OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
+	OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
 	if (!scene) {
 		return req->SendErrorResponse("requested scene doesn't exist");
 	}
 
-	OBSSceneItemAutoRelease sceneItem =
-		Utils::GetSceneItemFromName(scene, itemName);
+	OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName);
 	if (!sceneItem) {
 		return req->SendErrorResponse("specified scene item doesn't exist");
 	}
@@ -105,7 +104,7 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemProperties(WSRequestHandler*
 	}
 
 	QString sceneName = obs_data_get_string(req->data, "scene-name");
-	OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
+	OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
 	if (!scene) {
 		return req->SendErrorResponse("requested scene doesn't exist");
 	}
@@ -283,7 +282,7 @@ HandlerResponse WSRequestHandler::HandleResetSceneItem(WSRequestHandler* req) {
 	}
 
 	const char* sceneName = obs_data_get_string(req->data, "scene-name");
-	OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
+	OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
 	if (!scene) {
 		return req->SendErrorResponse("requested scene doesn't exist");
 	}
@@ -329,7 +328,7 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemRender(WSRequestHandler* req
 	}
 
 	const char* sceneName = obs_data_get_string(req->data, "scene-name");
-	OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
+	OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
 	if (!scene) {
 		return req->SendErrorResponse("requested scene doesn't exist");
 	}
@@ -371,7 +370,7 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemPosition(WSRequestHandler* r
 	}
 
 	QString sceneName = obs_data_get_string(req->data, "scene-name");
-	OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
+	OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
 	if (!scene) {
 		return req->SendErrorResponse("requested scene could not be found");
 	}
@@ -419,7 +418,7 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemTransform(WSRequestHandler*
 	}
 
 	QString sceneName = obs_data_get_string(req->data, "scene-name");
-	OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
+	OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
 	if (!scene) {
 		return req->SendErrorResponse("requested scene doesn't exist");
 	}
@@ -471,7 +470,7 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemCrop(WSRequestHandler* req)
 	}
 
 	QString sceneName = obs_data_get_string(req->data, "scene-name");
-	OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
+	OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
 	if (!scene) {
 		return req->SendErrorResponse("requested scene doesn't exist");
 	}
@@ -511,7 +510,7 @@ HandlerResponse WSRequestHandler::HandleDeleteSceneItem(WSRequestHandler* req) {
 	}
 
 	const char* sceneName = obs_data_get_string(req->data, "scene");
-	OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
+	OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
 	if (!scene) {
 		return req->SendErrorResponse("requested scene doesn't exist");
 	}
@@ -564,13 +563,13 @@ HandlerResponse WSRequestHandler::HandleDuplicateSceneItem(WSRequestHandler* req
 	}
 
 	const char* fromSceneName = obs_data_get_string(req->data, "fromScene");
-	OBSSourceAutoRelease fromScene = Utils::GetSceneFromNameOrCurrent(fromSceneName);
+	OBSScene fromScene = Utils::GetSceneFromNameOrCurrent(fromSceneName);
 	if (!fromScene) {
 		return req->SendErrorResponse("requested fromScene doesn't exist");
 	}
 
 	const char* toSceneName = obs_data_get_string(req->data, "toScene");
-	OBSSourceAutoRelease toScene = Utils::GetSceneFromNameOrCurrent(toSceneName);
+	OBSScene toScene = Utils::GetSceneFromNameOrCurrent(toSceneName);
 	if (!toScene) {
 		return req->SendErrorResponse("requested toScene doesn't exist");
 	}
@@ -586,7 +585,7 @@ HandlerResponse WSRequestHandler::HandleDuplicateSceneItem(WSRequestHandler* req
 	data.referenceItem = referenceItem;
 
 	obs_enter_graphics();
-	obs_scene_atomic_update(obs_scene_from_source(toScene), DuplicateSceneItem, &data);
+	obs_scene_atomic_update(toScene, DuplicateSceneItem, &data);
 	obs_leave_graphics();
 
 	obs_sceneitem_t *newItem = data.newItem;
@@ -600,7 +599,7 @@ HandlerResponse WSRequestHandler::HandleDuplicateSceneItem(WSRequestHandler* req
 
 	OBSDataAutoRelease responseData = obs_data_create();
 	obs_data_set_obj(responseData, "item", itemData);
-	obs_data_set_string(responseData, "scene", obs_source_get_name(toScene));
+	obs_data_set_string(responseData, "scene", obs_source_get_name(obs_scene_get_source(toScene)));
 
 	return req->SendOKResponse(responseData);
 }
diff --git a/src/WSRequestHandler_Scenes.cpp b/src/WSRequestHandler_Scenes.cpp
index 6e8429c8..3a8754b4 100644
--- a/src/WSRequestHandler_Scenes.cpp
+++ b/src/WSRequestHandler_Scenes.cpp
@@ -94,7 +94,7 @@ HandlerResponse WSRequestHandler::HandleGetSceneList(WSRequestHandler* req) {
 */
 HandlerResponse WSRequestHandler::HandleReorderSceneItems(WSRequestHandler* req) {
 	QString sceneName = obs_data_get_string(req->data, "scene");
-	OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
+	OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
 	if (!scene) {
 		return req->SendErrorResponse("requested scene doesn't exist");
 	}
@@ -104,37 +104,46 @@ HandlerResponse WSRequestHandler::HandleReorderSceneItems(WSRequestHandler* req)
 		return req->SendErrorResponse("sceneItem order not specified");
 	}
 
-	size_t count = obs_data_array_count(items);
+	struct reorder_context {
+		obs_data_array_t* items;
+		bool success;
+		QString errorMessage;
+	};
 
-	std::vector<obs_sceneitem_t*> newOrder;
-	newOrder.reserve(count);
+	struct reorder_context ctx;
+	ctx.success = false;
+	ctx.items = items;
 
-	for (size_t i = 0; i < count; ++i) {
-		OBSDataAutoRelease item = obs_data_array_item(items, i);
+	obs_scene_atomic_update(scene, [](void* param, obs_scene_t* scene) {
+		auto ctx = reinterpret_cast<struct reorder_context*>(param);
 
-		OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromItem(scene, item);
-		obs_sceneitem_release(sceneItem); // ref dec
+		QVector<struct obs_sceneitem_order_info> orderList;
+		struct obs_sceneitem_order_info info;
 
-		if (!sceneItem) {
-			return req->SendErrorResponse("Invalid sceneItem id or name specified");
-		}
-		
-		for (size_t j = 0; j <= i; ++j) {
-			if (sceneItem == newOrder[j]) {
-				return req->SendErrorResponse("Duplicate sceneItem in specified order");
+		size_t itemCount = obs_data_array_count(ctx->items);
+		for (int i = 0; i < itemCount; i++) {
+			OBSDataAutoRelease item = obs_data_array_item(ctx->items, i);
+
+			OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromItem(scene, item);
+			if (!sceneItem) {
+				ctx->success = false;
+				ctx->errorMessage = "Invalid sceneItem id or name specified";
+				return;
 			}
+
+			info.group = nullptr;
+			info.item = sceneItem;
+			orderList.insert(0, info);
 		}
 
-		newOrder.push_back(sceneItem);
-	}
+		ctx->success = obs_scene_reorder_items2(scene, orderList.data(), orderList.size());
+		if (!ctx->success) {
+			ctx->errorMessage = "Invalid sceneItem order";
+		}
+	}, &ctx);
 
-	bool success = obs_scene_reorder_items(obs_scene_from_source(scene), newOrder.data(), count);
-	if (!success) {
-		return req->SendErrorResponse("Invalid sceneItem order");
-	}
-
-	for (auto const& item: newOrder) {
-		obs_sceneitem_release(item);
+	if (!ctx.success) {
+		return req->SendErrorResponse(ctx.errorMessage);
 	}
 
 	return req->SendOKResponse();
diff --git a/src/WSRequestHandler_StudioMode.cpp b/src/WSRequestHandler_StudioMode.cpp
index 738b5bcc..bfcdae4e 100644
--- a/src/WSRequestHandler_StudioMode.cpp
+++ b/src/WSRequestHandler_StudioMode.cpp
@@ -69,12 +69,12 @@ HandlerResponse WSRequestHandler::HandleSetPreviewScene(WSRequestHandler* req) {
 	}
 
 	const char* scene_name = obs_data_get_string(req->data, "scene-name");
-	OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(scene_name);
+	OBSScene scene = Utils::GetSceneFromNameOrCurrent(scene_name);
 	if (!scene) {
 		return req->SendErrorResponse("specified scene doesn't exist");
 	}
 
-	obs_frontend_set_current_preview_scene(scene);
+	obs_frontend_set_current_preview_scene(obs_scene_get_source(scene));
 	return req->SendOKResponse();
 }
 
diff --git a/src/obs-websocket.cpp b/src/obs-websocket.cpp
index 7ec92245..2f066ac0 100644
--- a/src/obs-websocket.cpp
+++ b/src/obs-websocket.cpp
@@ -18,6 +18,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
 
 #include <obs-module.h>
 #include <obs-frontend-api.h>
+#include <obs-data.h>
 
 #include <QtCore/QTimer>
 #include <QtWidgets/QAction>