Skip to main content
Lesson 8 of 12 · xAI Security & Compliance

ACLs and the Principle of Least Privilege

Lesson 8 of 12 Updated July 28, 2026 Advanced
On this page
  1. The Principle of Least Privilege Applied to APIs
  2. xAI’s ACL System
  3. Rate Limits: An Additional Security Layer
  4. A Key Strategy Per Environment
  5. Verify and Audit Permissions
  6. Key Takeaways

The Principle of Least Privilege Applied to APIs

The principle of least privilege is one of the oldest concepts in cybersecurity: every component of a system should only have access to the resources strictly necessary for its operation. The idea is intuitive when you bring it back to the physical world—you don’t give the master key to the building to the person watering the lobby plants. Applied to xAI API keys, this means each key must be limited to only the endpoints and models the application carrying it actually needs. If this key ever leaks, the attacker inherits its permissions: a key restricted to chat with a single model causes infinitely less damage than a key that opens the entire catalog.

xAI implements this principle through a granular ACL (Access Control List) system, which we’ll detail below.

xAI’s ACL System

ACLs follow the format api-key:{type}:{value}. Two types concern you day-to-day: api-key:endpoint:{endpoint} restricts accessible endpoints, and api-key:model:{model_id} restricts usable models. There is also a wildcard, api-key:endpoint:*, which grants access to all endpoints—convenient for development, prohibited in production, precisely because it negates the benefit of least privilege.

The endpoints you can authorize individually are: chat (chat completion, by far the most common), embed (embedding generation), image (image generation and editing), models (model list consultation), tokenize (text tokenization), documents (document management), and sample (sampling).

Let’s see what this looks like in a real-world example. Imagine a customer support chatbot that uses only the grok-4.3 model in chat mode. Its ideal key looks like this:

{
  "name": "support-chatbot-prod",
  "acls": [
    "api-key:endpoint:chat",
    "api-key:model:grok-4.3"
  ],
  "qps": 5,
  "qpm": 100,
  "tpm": 500000,
  "expireTime": "2026-12-31T23:59:59Z"
}

Read this configuration as a contract: the key can only access the chat endpoint—no images, no embeddings, so no possible misuse for those purposes. It can only use grok-4.3, which prevents accidental (or malicious) calls to more expensive models. It is capped at 5 requests per second and 100 per minute, and it will expire on a known date. If a developer on the project ever tries to add image generation without telling anyone, the API will refuse: the technical constraint enforces the architectural decision.

Rate Limits: An Additional Security Layer

ACLs answer the question “what,” rate limits answer “how much.” QPS (Queries Per Second) caps requests per second: this is your safeguard against accidental spikes—the infinite loop introduced by a refactor on a Friday night, or a DDoS attack bouncing off your application. QPM (Queries Per Minute) smooths traffic over a longer period and catches abuse that would stay under the QPS radar. TPM (Tokens Per Minute), finally, caps the volume of tokens consumed: it protects against very long individual requests—a user pasting a 200-page document into your chatbot—that would exhaust your budget in minutes without ever exceeding the authorized number of requests.

A Key Strategy Per Environment

In practice, you don’t configure a single key but a family of keys, and each environment has its own logic. In development, you reverse the production logic: broad access, tight limits.

{
  "name": "dev-team-alpha",
  "acls": ["api-key:endpoint:*", "api-key:model:*"],
  "qps": 2,
  "qpm": 30,
  "tpm": 100000,
  "expireTime": "2026-06-30T23:59:59Z"
}

Developers can explore everything, but low limits avoid billing surprises if a test goes wrong. In staging, on the contrary, you replicate the production ACLs exactly (api-key:endpoint:chat, api-key:model:grok-4.3, QPS 5, QPM 100, TPM 500000): this is the only way to detect a permissions issue upstream—if staging code attempts a call that production will refuse, better to discover it now than in the first hour of going live. The production key, however, combines staging’s strict permissions with limits sized for real traffic (for example QPS 20, QPM 600, TPM 2000000) and a scheduled expiration—"expireTime": "2027-03-31T23:59:59Z" on a key named prod-chatbot-v2.

Verify and Audit Permissions

To see what an active key is actually allowed to do, query the API with the key itself:

curl https://api.x.ai/v1/api-key \
  -H "Authorization: Bearer $XAI_API_KEY"

The response lists effective ACLs, the api_key_blocked and api_key_disabled statuses, a masked version of the key (redacted_api_key), and the team_id. Make a habit of regularly comparing these ACLs to the application’s actual needs: permissions tend to accumulate over time, never shrinking spontaneously. On the models side, the GET /auth/teams/{teamId}/models endpoint returns the list of models available to your team with their pricing—valuable information for defining relevant model ACLs and ruling out expensive models you don’t use.

To cement all this, perform a full audit of your keys. List active keys for each team, then query each one: are its ACLs minimal, without wildcards in production? Are its rate limits reasonable? Is its expiration date set? Does its name identify the application and environment—“prod-chatbot-v2” tells a story, “key-test-2” tells none? Delete orphaned keys and document the result: this document will serve as a starting point for the next audit, and you’ll see at a glance what has drifted in the meantime.

Key Takeaways

  • Each key must have restrictive ACLs: only necessary endpoints and models
  • Never use a * wildcard in production
  • Combine ACLs with rate limits (QPS, QPM, TPM) adapted to real usage
  • Create distinct keys per environment (dev, staging, prod) and per application—staging with the same ACLs as prod
  • Regularly audit permissions on your active keys via GET /v1/api-key