Why you shouldn't reward players for looking at ads

Paying players to stand by billboards or walk through ad portals inflates impressions, which can cost your ad earnings. What Roblox allows instead.

warningLuaudit check: rewarded-immersive-ads

What it looks like

The game shows Roblox immersive ads, through an AdGui on a part or an AdPortal, and pays players to generate the views or teleports it earns from: coins for standing near the billboard, a bonus for keeping the camera on it, or a reward for walking through the ad portal.

Luau
if (root.Position - adBoard.Position).Magnitude < 25 then
	profile.Coins += 5
end

How to detect it

  • Find every AdGui and AdPortal in the place, and every script that reads their position, their part, or which way the camera faces.
  • Look for rewards tied to them: currency, items or progress for being near an ad unit, looking at it, standing in a zone built around it, or entering a portal ad. Also look for code that turns the camera towards an ad, moves players in front of one, or teleports them into a portal.
  • Roblox's immersive ads guide says publishers earn per image or video impression, which counts when a player looks at the ad for long enough at a large enough size, and per teleport through a portal ad. It warns that inflating impressions or teleports can lead Roblox to deduct or reclaim Robux and suspend the game or account.
  • The same guide names one way to reward players for ads: rewarded video ads, which pay out through a developer product receipt. A reward for anything else to do with an immersive ad pays players for impressions or teleports they wouldn't otherwise make.
  • Ad units placed where players naturally look, such as a lobby wall, with nothing that rewards or forces the view, are fine.

What an exploiter could do

No exploit is needed, and the risk falls on the developer. Players who are paid to look at ads or walk through portals produce impressions and teleports that exist only for the reward, which the guide's warning about inflated numbers covers. The game can lose its ad earnings, have Robux reclaimed, or be suspended. Players can also farm the reward by idling at the board.

How to fix it

  • Remove rewards, camera moves and teleports tied to immersive ad units, and let players see ads as part of the world.
  • To reward players for ads, use rewarded video ads and grant the prize in the receipt handler, as Rewarded ad prizes granted outside the receipt describes.

Vulnerable example

Vulnerable
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
local PlayerData = require(script.Parent.PlayerData)

local adBoard = Workspace:WaitForChild("AdBoard") :: BasePart
local range = 25
local payout = 5

while true do
	task.wait(10)
	for _, player in Players:GetPlayers() do
		local root = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
		local profile = PlayerData.get(player)
		if root and root:IsA("BasePart") and profile and (root.Position - adBoard.Position).Magnitude < range then
			profile.Coins += payout
		end
	end
end

Fixed example

Fixed
local AdService = game:GetService("AdService")
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PlayerData = require(script.Parent.PlayerData)

local adProduct = 1100031
local payout = 50
local showing: { [Player]: boolean } = {}

ReplicatedStorage.Remotes.WatchAdForCoins.OnServerEvent:Connect(function(player: Player)
	if showing[player] then
		return
	end
	showing[player] = true
	pcall(function()
		return AdService:ShowRewardedVideoAdAsync(player, AdService:CreateAdRewardFromDevProductId(adProduct))
	end)
	showing[player] = nil
end)

Players.PlayerRemoving:Connect(function(player)
	showing[player] = nil
end)

MarketplaceService.ProcessReceipt = function(receipt)
	if receipt.ProductId ~= adProduct then
		return Enum.ProductPurchaseDecision.NotProcessedYet
	end
	local player = Players:GetPlayerByUserId(receipt.PlayerId)
	local profile = player and PlayerData.get(player)
	if not profile then
		return Enum.ProductPurchaseDecision.NotProcessedYet
	end
	local key = "Purchase:" .. receipt.PurchaseId
	if not profile.Claimed[key] then
		profile.Claimed[key] = true
		profile.Coins += payout
	end
	return Enum.ProductPurchaseDecision.PurchaseGranted
end

Common false positives

  • Rewards from rewarded video ads, granted in the receipt handler, are the supported way to reward ad views. Judge them with Rewarded ad prizes granted outside the receipt.
  • Rewards near an ad unit for something unrelated, such as a checkpoint or shop that happens to sit beside a billboard, aren't this finding unless the ad is the reason for the reward.
  • The game's own posters, sponsor boards and announcements aren't Roblox immersive ads. Judge those with Sponsored content shown without checking AreAdsAllowed.