rozod
    Preparing search index...

    rozod

    RoZod

    Type-safe Roblox API and OpenCloud client for TypeScript

    npm bundle size npm npm

    AboutFeaturesInstallationQuick StartUsageOpenCloudCreditsDocumentation


    RoZod makes working with Roblox APIs simple and type-safe in TypeScript. With 650+ classic Roblox web API endpoints and 95+ OpenCloud endpoints (all code-generated from official Roblox documentation), you get comprehensive coverage of virtually every available Roblox API with full type safety.

    Perfect for everything from small one-time NodeJS/Bun/Deno scripts to large-scale production applications. RoZod powers RoGold, a browser extension with 800,000+ active users, handling millions of API requests daily across both frontend extensions and backend workflows.

    • Simple Interface - Easy to understand API with minimal boilerplate
    • 🔒 Type Safety - Complete TypeScript type safety for requests and responses
    • 📚 750+ Total Endpoints - 650+ classic web APIs + 95+ OpenCloud APIs, all code-generated from official docs
    • 🚀 Production Ready - Battle-tested in applications serving 800,000+ users
    • 🔄 Pagination Helpers - Easy tools for handling paginated responses
    • 🔁 Batch Processing - Split large requests automatically to avoid API limits
    • 🌐 Universal Runtime Support - Works seamlessly in NodeJS, Bun, Deno, and browsers
    • 🔍 Custom Endpoints - Define your own endpoints with full type safety
    • 🧩 Smart Error Handling - Choose between safe unions or throw-on-error
    npm install rozod
    # or
    yarn add rozod
    # or
    pnpm add rozod
    import { fetchApi } from 'rozod';
    import { getUsersUserdetails } from 'rozod/lib/endpoints/usersv1';

    // Fetch user details with full type safety
    const userInfo = await fetchApi(getUsersUserdetails, { userIds: [1, 123456] });
    if (isAnyErrorResponse(userInfo)) {
    return;
    }
    console.log(userInfo.data[0].displayName); // Properly typed!
    import { fetchApi } from 'rozod';
    import { getGamesIcons } from 'rozod/lib/endpoints/gamesv1';

    const response = await fetchApi(getGamesIcons, { universeIds: [1534453623, 65241] });
    console.log(response.data);
    import { fetchApiPages } from 'rozod';
    import { getGroupsGroupidWallPosts } from 'rozod/lib/endpoints/groupsv2';

    // Automatically fetches all pages
    const allPosts = await fetchApiPages(getGroupsGroupidWallPosts, { groupId: 11479637 });
    console.log(`Found ${allPosts.length} wall posts`);
    import { fetchApiPagesGenerator } from 'rozod';
    import { getGroupsGroupidWallPosts } from 'rozod/lib/endpoints/groupsv2';

    // Process pages as they arrive
    const pages = fetchApiPagesGenerator(getGroupsGroupidWallPosts, { groupId: 11479637 });
    for await (const page of pages) {
    console.log(`Processing page with ${page.data.length} posts`);
    // Do something with this page
    }
    import { fetchApiSplit } from 'rozod';
    import { getGamesIcons } from 'rozod/lib/endpoints/gamesv1';

    // Will automatically split into smaller batches of 100 universeIds per request
    const data = await fetchApiSplit(
    getGamesIcons,
    { universeIds: [1, 2, 3, 4, 5 /* many more IDs */] },
    { universeIds: 100 },
    );
    console.log(data);

    By default, requests return either the success type or a simple AnyError. Use the tiny helper to check:

    import { fetchApi, isAnyErrorResponse } from 'rozod';
    import { getGamesIcons } from 'rozod/lib/endpoints/gamesv1';

    const res = await fetchApi(getGamesIcons, { universeIds: [1534453623] });
    if (isAnyErrorResponse(res)) {
    console.error(res.message);
    } else {
    console.log(res.data);
    }

    Prefer a straight try/catch? Enable throwing:

    try {
    const res = await fetchApi(getGamesIcons, { universeIds: [1534453623] }, { throwOnError: true });
    console.log(res.data);
    } catch (err) {
    console.error((err as Error).message);
    }

    Need the raw Response? Use returnRaw: true:

    const resp = await fetchApi(getGamesIcons, { universeIds: [1534453623] }, { returnRaw: true });
    const json = await resp.json();

    RoZod supports Roblox's newer OpenCloud APIs with the same easy interface:

    import { fetchApi } from 'rozod';
    import { v2 } from 'rozod/lib/opencloud';

    // Get universe details through OpenCloud
    const universeInfo = await fetchApi(v2.getCloudV2UniversesUniverseId, {
    universe_id: '123456789',
    });

    // Access typed properties
    console.log(universeInfo.displayName);
    console.log(universeInfo.description);
    import { fetchApi } from 'rozod';
    import { getCloudV2UniversesUniverseIdDataStoresDataStoreIdEntries } from 'rozod/lib/opencloud/v2/cloud';

    // Get DataStore entries with type safety
    const dataStoreEntries = await fetchApi(getCloudV2UniversesUniverseIdDataStoresDataStoreIdEntries, {
    universe_id: '123456789',
    data_store_id: 'MyStore',
    });

    RoZod handles Roblox authentication automatically with comprehensive security features:

    In browsers, authentication works automatically when users are logged into Roblox:

    import { fetchApi } from 'rozod';
    import { getUsersUserdetails } from 'rozod/lib/endpoints/usersv1';

    // Cookies are sent automatically - no setup required!
    const userInfo = await fetchApi(getUsersUserdetails, { userIds: [123456] });

    For server environments, use configureServer() to set up authentication once:

    import { configureServer, fetchApi } from 'rozod';
    import { getUsersUserdetails } from 'rozod/lib/endpoints/usersv1';

    // Configure once at startup
    configureServer({ cookies: 'your_roblosecurity_cookie_here' });

    // All subsequent requests automatically include the cookie
    const userInfo = await fetchApi(getUsersUserdetails, { userIds: [123456] });

    Use multiple Roblox accounts for load distribution or fallback:

    import { configureServer } from 'rozod';

    // Multiple accounts with round-robin rotation (default)
    configureServer({
    cookies: [
    'account1_roblosecurity_cookie',
    'account2_roblosecurity_cookie',
    'account3_roblosecurity_cookie',
    ],
    });

    // Requests automatically cycle through accounts: 1 → 2 → 3 → 1 → 2 → ...

    Control how cookies and user agents are selected:

    import { configureServer } from 'rozod';

    configureServer({
    cookies: ['cookie1', 'cookie2', 'cookie3'],
    cookieRotation: 'round-robin', // Cycle sequentially (default for multiple)
    // cookieRotation: 'random', // Pick randomly per request
    // cookieRotation: 'none', // Always use first cookie

    userAgents: ['CustomBot/1.0', 'CustomBot/2.0'], // Optional custom UAs
    userAgentRotation: 'none', // Consistent per session (default)
    // userAgentRotation: 'random',
    // userAgentRotation: 'round-robin',
    });

    RoZod includes built-in browser user agents applied automatically in server environments. Customize or disable:

    // Use custom user agents
    configureServer({
    cookies: '...',
    userAgents: ['MyBot/1.0', 'MyService/2.0'],
    userAgentRotation: 'round-robin',
    });

    // Disable user agent injection
    configureServer({ cookies: '...', userAgents: [] });

    For OpenCloud endpoints (apis.roblox.com), set your API key once:

    import { configureServer } from 'rozod';
    import { v2 } from 'rozod/lib/opencloud';

    // Configure OpenCloud API key
    configureServer({ cloudKey: 'your_opencloud_api_key_here' });

    // All OpenCloud requests automatically include x-api-key header
    const universeInfo = await fetchApi(v2.getCloudV2UniversesUniverseId, {
    universe_id: '123456789',
    });

    You can configure both classic API cookies and OpenCloud keys together:

    configureServer({
    cookies: ['account1', 'account2'], // For classic *.roblox.com APIs
    cloudKey: 'your_opencloud_key', // For apis.roblox.com
    });

    Note: The API key is only applied to OpenCloud endpoints (URLs containing /cloud/). Cookies are applied to all other Roblox APIs, including undocumented cookie-based APIs on apis.roblox.com.

    import { configureServer, clearServerConfig, getServerConfig } from 'rozod';

    // Check current configuration
    const config = getServerConfig();
    console.log(config.cookies, config.cloudKey);

    // Clear all server configuration
    clearServerConfig();

    You can still pass headers manually per-request if needed:

    const userInfo = await fetchApi(
    getUsersUserdetails,
    { userIds: [123456] },
    {
    headers: {
    'Cookie': '.ROBLOSECURITY=your_cookie_here'
    }
    }
    );

    Note: Manual headers take precedence over configureServer() defaults.

    RoZod automatically handles advanced Roblox security requirements:

    • ✅ XCSRF Token Management - Automatic CSRF token retrieval and caching
    • ✅ Hardware-Backed Authentication - Full HBA signature support
    • ✅ Challenge Handling - Captchas, 2FA, and other authentication challenges
    • ✅ Cookie Security - Secure cookie parsing and validation
    • ✅ Cookie Rotation - Automatic handling of Roblox's cookie rotation

    Roblox is gradually implementing .ROBLOSECURITY cookie rotation for improved security. RoZod automatically detects when cookies are rotated and can notify you to persist the new values:

    import { configureServer, refreshCookie, getCookies } from 'rozod';

    configureServer({
    cookies: process.env.ROBLOX_COOKIE,
    onCookieRefresh: async ({ oldCookie, newCookie, poolIndex }) => {
    // Roblox rotated the cookie - persist the new value
    await db.updateCookie(poolIndex, newCookie);
    console.log('Cookie rotated and saved!');
    }
    });

    The internal cookie pool updates automatically, so the callback is only needed if you want to persist cookies across restarts.

    You can also proactively refresh cookies before they expire:

    // Refresh on a schedule or before important operations
    const result = await refreshCookie(0);
    if (result.success) {
    await db.updateCookie(0, result.newCookie);
    }

    // Utility functions
    const allCookies = getCookies(); // Get current cookie values
    updateCookie(0, 'new_value'); // Manually update a cookie

    For advanced authentication challenges (captchas, 2FA), set up a global challenge handler:

    import { setHandleGenericChallenge } from 'rozod';

    setHandleGenericChallenge(async (challenge) => {
    // Handle captcha, 2FA, or other challenges
    // Return the challenge response or undefined to skip
    if (challenge.challengeType === 'captcha') {
    const solution = await solveCaptcha(challenge.challengeId);
    return {
    challengeType: challenge.challengeType,
    challengeId: challenge.challengeId,
    challengeBase64Metadata: solution
    };
    }
    });

    For Node.js environments requiring custom HBA keys:

    import { changeHBAKeys } from 'rozod';

    // Provide your own crypto key pair for HBA signatures
    const keyPair = await crypto.subtle.generateKey(
    { name: 'ECDSA', namedCurve: 'P-256' },
    true,
    ['sign', 'verify']
    );

    changeHBAKeys(keyPair);

    OpenCloud APIs require API keys. Use configureServer() for automatic injection:

    import { configureServer, fetchApi } from 'rozod';
    import { v2 } from 'rozod/lib/opencloud';

    // Configure once at startup
    configureServer({ cloudKey: 'your_opencloud_api_key_here' });

    // All OpenCloud requests automatically include x-api-key
    const universeInfo = await fetchApi(v2.getCloudV2UniversesUniverseId, {
    universe_id: '123456789',
    });

    Or pass headers manually per-request:

    const universeInfo = await fetchApi(
    v2.getCloudV2UniversesUniverseId,
    { universe_id: '123456789' },
    {
    headers: {
    'x-api-key': 'your_opencloud_api_key_here'
    }
    }
    );

    You can define custom endpoints for your specific needs:

    import { z } from 'zod';
    import { endpoint, fetchApi } from 'rozod';

    const myCustomEndpoint = endpoint({
    method: 'GET',
    path: '/v1/custom/:customId',
    baseUrl: 'https://my-api.example.com',
    parameters: {
    customId: z.string(),
    optional: z.string().optional(),
    },
    response: z.object({
    success: z.boolean(),
    data: z.array(z.string()),
    }),
    });

    const response = await fetchApi(myCustomEndpoint, { customId: '123' });

    This repository is maintained by Alrovi ApS, the company behind RoGold.

    RoZod is not affiliated with, maintained, authorized, endorsed, or sponsored by Roblox Corporation or any of its affiliates.