Saved Sep 28, 2026 by @enviglo: API rate limits, the answers the API gives, and codes following the dashboard's rules
## Troubleshooting- - **Nothing is recorded.** Check **Allow HTTP Requests** is on, the key has **Grant and revoke licenses**, and the ID in `PASSES` is the product's Enviglo ID. A wrong ID answers 404: "Product not found in this store. Pass productId or robloxProductId."+ - **Nothing is recorded.** Check **Allow HTTP Requests** is on, the key has **Grant and revoke licenses**, and the ID in `PASSES` is the product's Enviglo ID. A wrong ID answers 404: "No product in this store has that productId."- **Enviglo answers `"created": false` with a revoked license.** Your store revoked this player's license. Restore it if they should have it.- - **429 "Too many licenses recorded in the last minute. Wait a moment and retry."** A key can record 300 a minute, repeats included. In a busy game, ask `GET /api/v1/licenses/check` first, which has no fixed limit, and only record passes Enviglo doesn't have yet. That needs the **Check licenses** scope on the key as well.+ - **429 "Over the limit for recording purchases made in game (300 a minute per key)."** A key can record 300 a minute, repeats included. In a busy game, ask `GET /api/v1/licenses?robloxId=` first, which only counts against the key's 1,200 requests a minute, and only record passes Enviglo doesn't have yet. That needs the **Check licenses** scope on the key as well. See [Rate limits](/wiki/api-quickstart#rate-limits).- **The product page shows Off-sale on Roblox.** Put the pass back on sale on Roblox, then press **Check Roblox** at the top of the product's **Delivery** page.
A game pass is bought once, from its own page on Roblox. This guide is for sellers who want to sell a product that way: linking the pass to your product, recording it from your game so the buyer gets their license, and what passes can't do.
licenses:write) scope. See API keys.Enviglo checks the pass with Roblox when you save. If something's wrong, the field says so:
Roblox decides the price. Enviglo reads it from the pass and shows it on your product page, so to change it, change the pass on Roblox.
The product page tells buyers the same thing: buy the pass on Roblox, then join your game, and it lands in their library once their Roblox account is linked. There's nothing else to press on Enviglo. Point buyers who haven't linked Roblox to .
When a player joins, the script asks Roblox whether they own each pass, and records the ones they do with POST /api/v1/licenses. It also records a pass bought while the player is in your game.
Put this in a Script in ServerScriptService. Replace YOUR_API_KEY with your key, and fill in PASSES with each game pass ID and the Enviglo product it gives. The product's Enviglo ID is the id that GET /api/v1/products returns for it.
-- EnvigloGamePasses: a Script in ServerScriptService.
-- It holds your API key, so keep it a server Script. Never a LocalScript, and never in ReplicatedStorage.
local HttpService = game:GetService("HttpService")
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local API_URL = "https://enviglo.com/api/v1"
local API_KEY = "YOUR_API_KEY" -- a key with the licenses:write scope
-- Each game pass, and the Enviglo product it gives: the product's id from GET /api/v1/products.
local PASSES = {
[123456789] = "YOUR_PRODUCT_ID",
}
-- Tells Enviglo this player owns a pass. Safe to repeat: Enviglo records a pass once per player.
local function recordPass(player, productId)
local sent, response = pcall(function()
return HttpService:RequestAsync({
Url = API_URL .. "/licenses",
Method = "POST",
Headers = {
["Authorization"] = "Bearer " .. API_KEY,
["Content-Type"] = "application/json",
},
-- No receiptId: a game pass isn't a receipt.
Body = HttpService:JSONEncode({
robloxId = tostring(player.UserId),
productId = productId,
}),
})
end)
if not sent then
warn("Enviglo: couldn't send the game pass:", response)
elseif not response.Success then
warn("Enviglo: HTTP " .. response.StatusCode .. ": " .. response.Body)
end
end
local function checkPasses(player)
for gamePassId, productId in pairs(PASSES) do
local ok, owns = pcall(function()
return MarketplaceService:UserOwnsGamePassAsync(player.UserId, gamePassId)
end)
if ok and owns then
recordPass(player, productId)
end
end
end
Players.PlayerAdded:Connect(checkPasses)
for _, player in ipairs(Players:GetPlayers()) do
task.spawn(checkPasses, player)
end
-- A pass bought while the player is in this game.
MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, gamePassId, wasPurchased)
local productId = PASSES[gamePassId]
if wasPurchased and productId then
recordPass(player, productId)
end
end)
Send no receiptId for a game pass. Enviglo keeps one record per player for each pass, so recording it every time they join is safe:
"created": true, and the purchase shows under Orders → Purchases with the game pass's ID."created": false and the license it already has.A pass is recorded once per player, and registering it again isn't a new purchase. So:
To keep pass licenses with the people who bought them, turn on Block customer transfers for this product on the product's Pricing page. Your team can still move a license.
PASSES is the product's Enviglo ID. A wrong ID answers 404: "No product in this store has that productId.""created": false with a revoked license. Your store revoked this player's license. Restore it if they should have it.GET /api/v1/licenses?robloxId= first, which only counts against the key's 1,200 requests a minute, and only record passes Enviglo doesn't have yet. That needs the Check licenses scope on the key as well. See Rate limits.