Saved Sep 26, 2026 by @enviglo: Store updates, version notes, leak protection, where views come from, API limits
## Rate limits- Each key can:+ There's no daily or monthly quota. A key can make as many requests as your game, bot or tools need, all day, every day. Only a few endpoints that write a lot have a limit, counted per key over the last minute or hour:- - record 300 purchases a minute with `POST /licenses`, counting repeats and grants;- - make 60 codes an hour, with `POST /codes`;- - ask for customer lists and stats 60 times a minute between them, with `GET /customers` and `GET /stats`.+ | What | Endpoint | Limit |+ | --- | --- | --- |+ | Record purchases and grants | `POST /licenses` | 300 a minute |+ | Customer lists and stats | `GET /customers` and `GET /stats` | 60 a minute, between them |+ | Make codes | `POST /codes` | 60 an hour |+ | Leak protection checks, which need no key | `POST /protection/check` | 600 a minute per address |+ | Everything else, including checking licenses | Every other endpoint | No fixed limit |- Past that it answers 429 until the time is up. The lookups a game makes when players join, like checking licenses, have no fixed limit, but ask once per player per server, not on a loop.+ Over a limit, the endpoint answers 429 with a `Retry-After` header saying how many seconds to wait, then works again as normal. Nothing carries over to the next day. Your plan changes how many keys a store can have, not how much each one can do.++ The lookups a game makes when players join, like checking licenses, have no fixed limit. Ask once per player per server and keep the answer, rather than asking on a loop.## Next steps…
Enviglo's API lets your game, bot or own tools work with your store: record purchases, give products, check who owns what, run codes, read customers and more. This guide gets you from a new key to a working request, and covers what you need to know before building on it.
You need an API key for your store. See API keys. Keep it on a server: never in a web page, a LocalScript or a public repository.
https://enviglo.com/api/v1. The API keys card in Settings → Integrations shows it too.Authorization: Bearer YOUR_API_KEY, or in an x-api-key header.Content-Type: application/json.GET /store answers with the store your key belongs to and the scopes the key has. Any key can call it, so it's the quickest way to check a key works:
curl "https://enviglo.com/api/v1/store" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"store": {
"id": "cls9x0",
"slug": "your-store",
"name": "Your Store",
"badges": ["VERIFIED"],
"robloxPlaceId"
_count is the store's published products, active licenses and followers.
The same call from a Roblox server script:
-- A Script in ServerScriptService. Server only: it holds your API key.
local HttpService = game:GetService("HttpService")
local sent, response = pcall(function()
return HttpService:RequestAsync({
Url = "https://enviglo.com/api/v1/store",
Method = "GET",
Headers = { ["Authorization"] = "Bearer YOUR_API_KEY" },
})
end)
if not sent then
warn("Couldn't reach Enviglo:", response)
elseif not response.Success then
warn("Enviglo answered HTTP " .. response.StatusCode .. ": " .. response.Body)
else
local data = HttpService:JSONDecode(response.Body)
print("This key belongs to " .. data.store.name)
end
Roblox needs Allow HTTP Requests switched on for this, under Game Settings → Security in Studio.
With the Read products scope, GET /products lists your published products, sorted by name, with the Roblox and Discord IDs a game or bot needs:
curl "https://enviglo.com/api/v1/products" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"products": [
{
"id": "clx7a1b2c",
"slug": "advanced-admin",
"name": "Advanced Admin",
"status": "PUBLISHED",
"licenseType": "SINGLE",
"robuxPrice": 400,
"stock": null,
"robloxProductId": "1234567890",
"robloxGamePassId": null,
"discordRoleId": null,
"activeLicenses": 318,
"url": "/s/your-store/advanced-admin"
}
]
}
That's trimmed: each product carries more fields. Add ?status=draft, ?status=archived or ?status=all to see other products. A product's id is what other endpoints mean by productId.
With the Grant and revoke licenses scope, POST /licenses gives someone one of your products, the same as Grant license in your dashboard. Name the product with productId, and the person with exactly one of these:
| Field | Who it names |
|---|---|
username | Their Enviglo username. |
customer | A customer, by the customer key from GET /customers. |
discordId | Their Discord user ID. |
email | Their email address. |
robloxId | Their Roblox user ID. On its own, this records a purchase made in your game. Send "source": "GRANT" with it to give instead. |
Except with robloxId, you can also send:
days: for a subscription, how many days to give, from 1 to 3,650. The product's own length unless you say.note: a private note of up to 500 characters, kept in the license's history.curl -X POST "https://enviglo.com/api/v1/licenses" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "email": "buyer@example.com", "productId": "clx7a1b2c", "note": "Giveaway winner" }'
To give by Roblox ID, the body is:
{ "robloxId": "99999999001", "productId": "clx7a1b2c", "source": "GRANT" }
It answers 201 with "created": true and the license, or 200 with "renewed": true when it added days to a subscription they already hold. What a grant gives depends on the product's license type, as in the dashboard: see Grant a license.
If the Discord or Roblox account is linked on Enviglo, or someone has confirmed that email address there, the product goes straight to their account. Anyone else has it waiting for them, and shows up under Customers in your dashboard like any other customer:
When a bot asks GET /discord/entitlements?discordUserId= which roles a member should have, the answer counts what you gave their Discord ID too, with "linked": false until they sign in with it or link it.
Only a robloxId can record a Roblox purchase, so "source": "ROBLOX_PURCHASE" with anyone else is refused. So is sending none of the five, or more than one: "Pass exactly one of robloxId, discordId, email, username or customer."
When a request goes wrong, the API answers with a status code and a JSON body saying what happened:
{ "error": "This key does not have the licenses:write scope." }
| Status | What it means | For example |
|---|---|---|
| 400 | Something in the request is wrong: a missing or malformed parameter, or a body that isn't JSON. | "Provide a numeric robloxId." |
| 401 | The key is missing, mistyped or revoked. | "Invalid or revoked API key." |
| 403 | The key doesn't have the scope this needs, or the store isn't open. | "This store is not active." |
| 404 | It isn't in this store. A key only ever sees its own store. | "Product not found in this store." |
| 409 | It can't happen as things stand, like publishing a product that isn't ready. | "Not ready to publish: …" |
| 429 | Too many requests. Wait a moment and try again. | "Too many licenses recorded in the last minute. Wait a moment and retry." |
Lists that can run long come a page at a time: licenses, codes and requests. Ask for up to 100 at once with limit; you get 50 if you don't ask. When there's more, the answer carries nextCursor. Send it back as cursor for the next page. A nextCursor of null means you have everything.
curl "https://enviglo.com/api/v1/licenses?status=active&limit=100" \
-H "Authorization: Bearer YOUR_API_KEY"
curl "https://enviglo.com/api/v1/licenses?status=active&limit=100&cursor=clx8f2k0a0001" \
-H "Authorization: Bearer YOUR_API_KEY"
Licenses and codes come newest first, and requests most recently updated first, so each page moves on to older items.
Customers are paged by number instead: page=1, page=2 and on, with nextPage in the answer saying what's next, or null when there isn't one.
There's no daily or monthly quota. A key can make as many requests as your game, bot or tools need, all day, every day. Only a few endpoints that write a lot have a limit, counted per key over the last minute or hour:
| What | Endpoint | Limit |
|---|---|---|
| Record purchases and grants | POST /licenses | 300 a minute |
| Customer lists and stats | GET /customers and GET /stats | 60 a minute, between them |
| Make codes | POST /codes | 60 an hour |
| Leak protection checks, which need no key | POST /protection/check | 600 a minute per address |
| Everything else, including checking licenses | Every other endpoint | No fixed limit |
Over a limit, the endpoint answers 429 with a Retry-After header saying how many seconds to wait, then works again as normal. Nothing carries over to the next day. Your plan changes how many keys a store can have, not how much each one can do.
The lookups a game makes when players join, like checking licenses, have no fixed limit. Ask once per player per server and keep the answer, rather than asking on a loop.