Cosmo API

API Documentation

The Cosmo API lets you integrate Luau obfuscation directly into your workflow. Protect your Roblox scripts programmatically with industry-leading VM and Shell obfuscation — from your own backend, CI/CD pipeline, or any HTTP client.

Quick Start

1
Create an account
Sign up at cosmo-rblx.online if you haven't already. You need an active plan to use the API.
2
Generate an API key
Navigate to Dashboard → Settings → API Keys and create a new key. Copy it immediately — it won't be shown again.
3
Make your first request
Use the example below to obfuscate a script with VM mode.
curl -X POST https://www.cosmo-rblx.online/api/obfuscate \
  -H "Authorization: Bearer cosmo_sk_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "print(\'Hello, Cosmo!\')",
    "mode": "vm"
  }'

Authentication

All API requests must include your API key in the Authorization header using the Bearer token scheme. Every request without a valid key will receive a 401 Unauthorized response.

Authorization: Bearer cosmo_sk_xxxxxxxxxxxxxxxx

API keys follow the format cosmo_sk_ followed by a 16-character alphanumeric string. Keys are tied to your account and inherit your plan's rate limits and quotas.

Never expose your API key in client-side code. Always route requests through your own backend server. If a key is compromised, rotate it immediately from your dashboard.

Setting Up Authenticated Clients

const COSMO_API_KEY = process.env.COSMO_API_KEY;
const BASE_URL = "https://www.cosmo-rblx.online";

async function cosmoRequest(endpoint, body) {
  const res = await fetch(`${BASE_URL}${endpoint}`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${COSMO_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

  if (!res.ok) {
    const err = await res.json();
    throw new Error(err.error || "Request failed");
  }

  return res.json();
}

const result = await cosmoRequest("/api/obfuscate", {
  source: myLuauCode,
  mode: "vm",
});

API Keys

API keys are managed from your Cosmo dashboard. Each key provides full access to the obfuscation endpoints within your plan's limits.

Generating a Key

1
Open your Dashboard
Log in and navigate to your account dashboard.
2
Go to Settings → API Keys
Find the API Keys section in your account settings.
3
Create New Key
Click Generate New Key. Give it a descriptive label (e.g., "Production Backend" or "CI Pipeline").
4
Copy and store securely
The full key is shown only once. Store it in your environment variables or secrets manager.

Key Permissions

All keys currently have full access to your account's API capabilities. Scoped permissions (read-only, specific endpoints) are on the roadmap. For now, treat every key as having full access and restrict distribution accordingly.

Rotating Keys

If a key is compromised or you want to rotate for security hygiene, generate a new key first, update all your services, then revoke the old key from the dashboard. This avoids downtime during rotation.

Use environment variables (COSMO_API_KEY) in all environments. Never commit keys to version control. Consider using a secrets manager like Vault, AWS Secrets Manager, or GitHub Encrypted Secrets for production deployments.

Protect User Scripts

Build a platform where users submit their Luau scripts for protection — without ever seeing the obfuscated output. Your backend handles obfuscation transparently, stores the protected code, and serves it when needed.

This is the recommended pattern for script marketplaces, loader platforms, and any service where end users should not have access to the raw obfuscated code. The API key stays on your server, and users never interact with Cosmo directly.

How It Works

User submits script
Your backend
Cosmo API
Store protected code
1
User uploads their Luau script on your platform
They paste or upload source code through your website UI.
2
Your backend sends it to Cosmo API
You choose the mode (vm or shell) and always enable macros. The user never sees these options unless you want them to.
3
Cosmo returns the obfuscated code
Your backend receives the protected output. The user never sees it.
4
Store and serve the protected version
Save the obfuscated code in your database. Serve it to Roblox loaders, key systems, or wherever needed.

Backend Implementation

The developer pre-configures the obfuscation mode and enables macros. Users only submit their source code — everything else is handled server-side.

const express = require("express");
const app = express();
app.use(express.json({ limit: "5mb" }));

const COSMO_KEY = process.env.COSMO_API_KEY;
const MODE = "vm";

app.post("/api/protect", async (req, res) => {
  try {
    const { source } = req.body;
    if (!source) {
      return res.status(400).json({ error: "Source code is required" });
    }

    const response = await fetch("https://www.cosmo-rblx.online/api/obfuscate", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${COSMO_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ source, mode: MODE, macros: true }),
    });

    if (!response.ok) {
      const err = await response.json();
      return res.status(response.status).json({ error: err.error });
    }

    const data = await response.json();

    await db.scripts.update(req.body.scriptId, {
      protected_code: data.obfuscated,
      protected_at: new Date(),
    });

    res.json({ success: true, message: "Script protected successfully" });
  } catch (err) {
    res.status(500).json({ error: "Protection failed" });
  }
});

app.listen(3000);

Letting Users Choose Mode

Optionally, you can let users pick between VM and Shell mode while keeping macros always enabled:

async function protectScript(source, mode = "vm") {
  const response = await fetch("/api/protect", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ source, mode, scriptId: currentScriptId }),
  });

  const data = await response.json();
  if (data.success) {
    showNotification("Script protected successfully!");
  }
}

Handling Script Updates

When a user updates their script, simply re-obfuscate by calling the same endpoint. The new protected version replaces the old one automatically.

app.put("/api/protect/:scriptId", async (req, res) => {
  const { source } = req.body;
  const { scriptId } = req.params;

  const script = await db.scripts.findById(scriptId);
  if (!script) {
    return res.status(404).json({ error: "Script not found" });
  }

  const response = await fetch("https://www.cosmo-rblx.online/api/obfuscate", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.COSMO_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      source,
      mode: script.mode,
      macros: true,
    }),
  });

  const data = await response.json();
  if (!response.ok) {
    return res.status(response.status).json({ error: data.error });
  }

  await db.scripts.update(scriptId, {
    source_code: source,
    protected_code: data.obfuscated,
    protected_at: new Date(),
    version: script.version + 1,
  });

  res.json({
    success: true,
    version: script.version + 1,
    message: "Script updated and re-protected",
  });
});
Never expose your API key in frontend code. Always proxy requests through your backend. If a key is compromised, rotate it immediately from the Cosmo dashboard.
For high-volume platforms, consider caching obfuscated output and only re-obfuscating when the source code changes. Compare hashes of the source to avoid unnecessary API calls and save on your plan limits.

Live Demo

Try Cosmo obfuscation right here — no API key needed. This demo uses a server-side protected endpoint with the same obfuscation engine used in production. The obfuscated output is never returned to the browser, just like in a real backend-to-backend integration.

Macros: always enabled
Loading editor...
131 / 2000 characters
This demo is limited to 2000 characters. For unlimited obfuscation, full API access, and production-ready integrations, check out our Pro and Cosmic plans.

VM Obfuscation

VM mode provides the strongest level of protection available. Your Luau source code is compiled into custom bytecode that runs inside Cosmo's proprietary virtual machine. This makes reverse engineering extremely difficult, as the original logic is never present in the output.

POST/api/obfuscate
ParameterTypeRequiredDescription
sourcestringYesThe Luau source code to obfuscate
modestringNo"vm" (default) — Virtual machine obfuscation
filenamestringNoFile name for error reporting (default: "script.lua")

Response

{
  "success": true,
  "obfuscated": "-- Obfuscated by Cosmo | cosmo-rblx.online\nlocal ...",
  "originalSize": 342,
  "obfuscatedSize": 12847,
  "mode": "vm",
  "usage": {
    "current": 15,
    "limit": 100,
    "bonus": 5
  }
}

Examples

curl -X POST https://www.cosmo-rblx.online/api/obfuscate \
  -H "Authorization: Bearer cosmo_sk_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "local Players = game:GetService(\'Players\')\nPlayers.PlayerAdded:Connect(function(player)\n  print(\'Welcome, \' .. player.Name)\nend)",
    "mode": "vm",
    "filename": "welcome.lua"
  }'

Shell Obfuscation

Shell mode wraps your code in multiple layers of encrypted shells. While not as deeply protective as VM mode, Shell obfuscation is significantly faster and produces more compact output — making it ideal for scripts that update frequently or where runtime performance is critical.

POST/api/obfuscate
ParameterTypeRequiredDescription
sourcestringYesThe Luau source code to obfuscate
modestringYes"shell" — Shell-based obfuscation
filenamestringNoFile name for error reporting (default: "script.lua")

When to Use VM vs Shell

VM Mode
  • Maximum security strength
  • Bytecode-level virtualization
  • Best for paid scripts & loaders
  • Slightly larger output size
  • Marginally slower execution
Shell Mode
  • Fast obfuscation speed
  • Multi-layer encryption shells
  • Best for frequent updates
  • Compact output
  • Faster runtime performance

Examples

curl -X POST https://www.cosmo-rblx.online/api/obfuscate \
  -H "Authorization: Bearer cosmo_sk_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "print(\'Hello from Shell mode!\')",
    "mode": "shell"
  }'

Check Usage

Retrieve your current usage statistics, plan details, and subscription status. Use this endpoint to monitor your quota before making obfuscation requests or to display usage information in your application.

GET/api/usage

Response

{
  "plan": "pro",
  "usage": {
    "current": 47,
    "limit": 500,
    "bonus": 20
  },
  "subscription": {
    "status": "active",
    "expiresAt": "2026-04-15T00:00:00.000Z"
  }
}

Examples

curl https://www.cosmo-rblx.online/api/usage \
  -H "Authorization: Bearer cosmo_sk_xxxxxxxxxxxxxxxx"

API Playground

Test Cosmo API endpoints directly from the documentation. Enter your API key, select an endpoint, configure your request, and send it live.

Request Builder
Loading editor...
Response
Send a request to see the response

Add to Your Website

Integrate Cosmo obfuscation into your web application by proxying requests through your backend. This keeps your API key secure and gives you full control over access.

Always call the Cosmo API from your backend server, never from the browser. CORS restrictions and API key security both require a server-side proxy approach.

Architecture

Your Frontend
Your Backend
Cosmo API

Next.js API Route

import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
  const { source, mode = "vm" } = await req.json();

  if (!source) {
    return NextResponse.json(
      { error: "Source code is required" },
      { status: 400 }
    );
  }

  const response = await fetch("https://www.cosmo-rblx.online/api/obfuscate", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.COSMO_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ source, mode }),
  });

  const data = await response.json();

  if (!response.ok) {
    return NextResponse.json(
      { error: data.error || "Obfuscation failed" },
      { status: response.status }
    );
  }

  return NextResponse.json(data);
}

Express.js Middleware

const express = require("express");
const app = express();

app.use(express.json({ limit: "5mb" }));

app.post("/api/obfuscate", async (req, res) => {
  try {
    const { source, mode = "vm" } = req.body;

    const response = await fetch("https://www.cosmo-rblx.online/api/obfuscate", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${process.env.COSMO_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ source, mode }),
    });

    const data = await response.json();
    res.status(response.status).json(data);
  } catch (err) {
    res.status(500).json({ error: "Internal server error" });
  }
});

app.listen(3000);

Flask Example

import os
import requests
from flask import Flask, request, jsonify

app = Flask(__name__)
COSMO_KEY = os.environ["COSMO_API_KEY"]

@app.route("/api/obfuscate", methods=["POST"])
def obfuscate():
    data = request.get_json()
    source = data.get("source")
    mode = data.get("mode", "vm")

    if not source:
        return jsonify({"error": "Source code is required"}), 400

    resp = requests.post(
        "https://www.cosmo-rblx.online/api/obfuscate",
        headers={"Authorization": f"Bearer {COSMO_KEY}"},
        json={"source": source, "mode": mode},
    )

    return jsonify(resp.json()), resp.status_code

if __name__ == "__main__":
    app.run(port=3000)

Frontend Integration

async function obfuscateScript(source, mode = "vm") {
  const response = await fetch("/api/obfuscate", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ source, mode }),
  });

  if (!response.ok) {
    const err = await response.json();
    throw new Error(err.error || "Failed to obfuscate");
  }

  return response.json();
}

const result = await obfuscateScript(editorContent, "vm");
outputEditor.setValue(result.obfuscated);

Roblox Integration

You can call the Cosmo API directly from Roblox game servers using HttpService. This is useful for on-demand obfuscation, dynamic script loaders, or building key systems.

Make sure to enable Allow HTTP Requests in your game's settings (Game Settings → Security). API keys stored in Roblox scripts should be kept server-side only — never distribute them in client-facing code.

Basic HttpService Request

local HttpService = game:GetService("HttpService")
local API_KEY = "cosmo_sk_xxxxxxxxxxxxxxxx"

local function obfuscate(source, mode)
    mode = mode or "vm"

    local success, result = pcall(function()
        return HttpService:RequestAsync({
            Url = "https://www.cosmo-rblx.online/api/obfuscate",
            Method = "POST",
            Headers = {
                ["Authorization"] = "Bearer " .. API_KEY,
                ["Content-Type"] = "application/json",
            },
            Body = HttpService:JSONEncode({
                source = source,
                mode = mode,
            }),
        })
    end)

    if success and result.Success then
        local data = HttpService:JSONDecode(result.Body)
        return data.obfuscated
    else
        warn("Obfuscation failed:", result)
        return nil
    end
end

local protected = obfuscate([[
    print("This script is now protected!")
]], "vm")

if protected then
    print("Obfuscation successful! Length:", #protected)
end

Key System Example

Build a key-based distribution system where scripts are obfuscated on-demand and served to authorized users.

local HttpService = game:GetService("HttpService")
local DataStoreService = game:GetService("DataStoreService")
local keyStore = DataStoreService:GetDataStore("LicenseKeys")
local API_KEY = "cosmo_sk_xxxxxxxxxxxxxxxx"

local SCRIPT_SOURCE = [[
local player = game.Players.LocalPlayer
-- Your protected script logic here
print("Licensed to: " .. player.Name)
]]

local function validateAndServe(player, licenseKey)
    local success, keyData = pcall(function()
        return keyStore:GetAsync(licenseKey)
    end)

    if not success or not keyData then
        return { success = false, error = "Invalid license key" }
    end

    if keyData.expiresAt and os.time() > keyData.expiresAt then
        return { success = false, error = "License expired" }
    end

    local personalizedSource = SCRIPT_SOURCE:gsub(
        "PLAYER_ID",
        tostring(player.UserId)
    )

    local ok, response = pcall(function()
        return HttpService:RequestAsync({
            Url = "https://www.cosmo-rblx.online/api/obfuscate",
            Method = "POST",
            Headers = {
                ["Authorization"] = "Bearer " .. API_KEY,
                ["Content-Type"] = "application/json",
            },
            Body = HttpService:JSONEncode({
                source = personalizedSource,
                mode = "vm",
            }),
        })
    end)

    if ok and response.Success then
        local data = HttpService:JSONDecode(response.Body)
        return { success = true, script = data.obfuscated }
    end

    return { success = false, error = "Obfuscation service unavailable" }
end

CI/CD Pipeline

Automate obfuscation as part of your build process. Protect all your scripts before deployment without manual intervention.

GitHub Actions

name: Obfuscate Scripts

on:
  push:
    branches: [main]
    paths:
      - "src/scripts/**"

jobs:
  obfuscate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Obfuscate Luau scripts
        env:
          COSMO_API_KEY: ${{ secrets.COSMO_API_KEY }}
        run: |
          mkdir -p dist/scripts
          for file in src/scripts/*.lua; do
            filename=$(basename "$file")
            echo "Obfuscating $filename..."
            
            source=$(cat "$file" | jq -Rs .)
            
            response=$(curl -s -X POST \
              https://www.cosmo-rblx.online/api/obfuscate \
              -H "Authorization: Bearer $COSMO_API_KEY" \
              -H "Content-Type: application/json" \
              -d "{\"source\": $source, \"mode\": \"vm\", \"filename\": \"$filename\"}")
            
            echo "$response" | jq -r '.obfuscated' > "dist/scripts/$filename"
            echo "Done: $filename"
          done

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: obfuscated-scripts
          path: dist/scripts/

Shell Script for Batch Obfuscation

#!/bin/bash
set -euo pipefail

INPUT_DIR="./src"
OUTPUT_DIR="./dist"
MODE="vm"

if [ -z "${COSMO_API_KEY:-}" ]; then
  echo "Error: COSMO_API_KEY not set"
  exit 1
fi

mkdir -p "$OUTPUT_DIR"

count=0
failed=0

for file in "$INPUT_DIR"/*.lua; do
  [ -f "$file" ] || continue
  filename=$(basename "$file")
  echo "[$((count + 1))] Obfuscating $filename..."

  source=$(cat "$file" | jq -Rs .)

  response=$(curl -s -w "\n%{http_code}" -X POST \
    https://www.cosmo-rblx.online/api/obfuscate \
    -H "Authorization: Bearer $COSMO_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"source\": $source, \"mode\": \"$MODE\", \"filename\": \"$filename\"}")

  http_code=$(echo "$response" | tail -1)
  body=$(echo "$response" | sed '$d')

  if [ "$http_code" = "200" ]; then
    echo "$body" | jq -r '.obfuscated' > "$OUTPUT_DIR/$filename"
    echo "  ✓ Saved to $OUTPUT_DIR/$filename"
    count=$((count + 1))
  else
    echo "  ✗ Failed ($http_code): $(echo "$body" | jq -r '.error // "Unknown"')"
    failed=$((failed + 1))
  fi

  sleep 0.5
done

echo ""
echo "Complete: $count succeeded, $failed failed"

Post-Build Hook (Node.js)

import fs from "fs/promises";
import path from "path";

const API_KEY = process.env.COSMO_API_KEY;
const INPUT_DIR = "./build/scripts";
const OUTPUT_DIR = "./build/protected";
const MODE = "vm";

async function obfuscateFile(filePath) {
  const source = await fs.readFile(filePath, "utf-8");
  const filename = path.basename(filePath);

  const res = await fetch("https://www.cosmo-rblx.online/api/obfuscate", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ source, mode: MODE, filename }),
  });

  if (!res.ok) {
    const err = await res.json();
    throw new Error(`${filename}: ${err.error}`);
  }

  const data = await res.json();
  return { filename, obfuscated: data.obfuscated, usage: data.usage };
}

async function main() {
  await fs.mkdir(OUTPUT_DIR, { recursive: true });
  const files = (await fs.readdir(INPUT_DIR)).filter((f) => f.endsWith(".lua"));

  console.log(`Obfuscating ${files.length} files...`);

  for (const file of files) {
    try {
      const result = await obfuscateFile(path.join(INPUT_DIR, file));
      await fs.writeFile(path.join(OUTPUT_DIR, result.filename), result.obfuscated);
      console.log(`  ✓ ${result.filename} (usage: ${result.usage.current}/${result.usage.limit})`);
    } catch (err) {
      console.error(`  ✗ ${err.message}`);
    }
  }
}

main();

Rate Limits

Rate limits are applied per account based on your active plan. Limits reset at midnight UTC each day. Bonus obfuscations carry over and are consumed after your daily limit is reached.

PlanDaily LimitRateBonus Eligible
Free10 / day5 req/minNo
Basic100 / day15 req/minYes
Pro500 / day30 req/minYes
EnterpriseUnlimited60 req/minYes

How Limits Work

Your daily counter resets at midnight UTC. Each successful obfuscation request counts as one unit regardless of input size. Failed requests (4xx or 5xx responses) do not count against your limit.

Bonus Obfuscations

Bonus obfuscations are extra credits earned through referrals, promotions, or plan upgrades. They persist across billing cycles and are consumed only after your daily limit is exhausted. Check your bonus balance via the /api/usage endpoint.

Response Headers

Every API response includes headers to help you track your usage programmatically:

X-RateLimit-Limit: 500
X-RateLimit-Remaining: 453
X-RateLimit-Reset: 1711497600
X-RateLimit-Bonus: 20

Error Codes

The API uses standard HTTP status codes. All error responses include a JSON body with an error field describing what went wrong.

CodeMeaningRecommended Action
400Bad RequestCheck your request body. Ensure source is a non-empty string.
401UnauthorizedVerify your API key is correct and included in the Authorization header.
403ForbiddenYour plan may have expired or the key has been revoked. Check your dashboard.
413Payload Too LargeReduce your source code size. Maximum input is 500KB.
429Rate LimitedYou've hit your daily limit. Wait for reset at midnight UTC or upgrade your plan.
500Server ErrorAn internal error occurred. Retry after a few seconds. Contact support if persistent.

Error Response Format

{
  "success": false,
  "error": "Rate limit exceeded. Daily limit of 100 obfuscations reached.",
  "code": 429,
  "retryAfter": 3600
}

Response Format

All API responses are JSON. The structure depends on whether the request succeeded or failed.

Success Response

{
  "success": true,
  "obfuscated": "-- Obfuscated by Cosmo | cosmo-rblx.online\n...",
  "originalSize": 1024,
  "obfuscatedSize": 15360,
  "mode": "vm",
  "usage": {
    "current": 48,
    "limit": 500,
    "bonus": 20
  }
}

Error Response

{
  "success": false,
  "error": "A description of what went wrong",
  "code": 400
}

Usage Object

The usage object is included in every successful obfuscation response so you can track consumption in real time.

ParameterTypeRequiredDescription
currentnumberYesNumber of obfuscations used today
limitnumberYesMaximum daily obfuscations for your plan
bonusnumberYesRemaining bonus obfuscation credits
When current reaches limit, bonus credits are consumed automatically. The bonus field decrements accordingly. Once both are exhausted, subsequent requests return a 429 status.