Authentication

Getting Your API Key

Every request to the Moonlit API must be authenticated with a subscription key. Book a call with our team at moonlit.ai/book-a-call to get your API key. We'll set up your account and provide your subscription key. Your key is a unique string that identifies your application and determines your access level and rate limits. Treat it like a password -- never expose it in client-side JavaScript, public Git repositories, CI/CD logs, or error messages. Each account can have multiple API keys. We recommend creating separate keys for development, staging, and production environments. This makes it easy to rotate a compromised key without affecting other environments.

Sending the Authentication Header

Include your key in every request using the Ocp-Apim-Subscription-Key HTTP header. This is the only authentication mechanism -- the API does not support query parameter authentication, Bearer tokens, or OAuth flows. The API will reject any request that omits this header or provides an invalid key with a 401 Unauthorized response. If your key is valid but does not have permission to access a specific endpoint (e.g., Luna endpoints on a basic plan), you will receive a 403 Forbidden response.

# cURL example
curl "https://api.moonlit.ai/v1.1/search/keyword_search" \
  -H "Content-Type: application/json" \
  -H "Ocp-Apim-Subscription-Key: YOUR_API_KEY" \
  -d '{"query": "huurrecht opzegging", "jurisdictions": ["Netherlands"]}'

# Python example
import requests

response = requests.post(
    "https://api.moonlit.ai/v1.1/search/hybrid_search",
    headers={
        "Ocp-Apim-Subscription-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"query": "GDPR consent requirements"},
)

# JavaScript example
const response = await fetch("https://api.moonlit.ai/v1.1/search/hybrid_search", {
  method: "POST",
  headers: {
    "Ocp-Apim-Subscription-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ query: "GDPR consent requirements" }),
});

Rate Limits

Each API key is subject to a monthly request quota determined by your plan. The default quota for the Developer plan is 50,000 requests per month. When you exceed this limit, the API returns a 429 Too Many Requests response. Rate limit headers are included in every response so you can track your usage programmatically: - X-RateLimit-Limit: Your monthly quota (e.g., 50000) - X-RateLimit-Remaining: Requests remaining in the current billing period - X-RateLimit-Reset: Unix timestamp when the quota resets (first of next month) All endpoints count equally toward your quota -- a filter request costs the same as a search request. Luna streaming sessions count as a single request per question, regardless of how many tokens are streamed. If you are approaching your limit, consider caching responses (especially filter endpoints, which change infrequently) or upgrading your plan. Enterprise plans offer custom quotas up to 10 million requests per month.

{
  "error": {
    "code": 429,
    "message": "Monthly request quota exceeded. Resets on 2026-04-01T00:00:00Z."
  }
}

Server-Side Only

The Moonlit API key must never be exposed to end users. Always make API calls from your backend server, not from client-side JavaScript running in the browser. If you are building a web application, create a backend API route that proxies requests to Moonlit. Your frontend calls your backend, and your backend calls Moonlit with the API key stored securely in server-side environment variables. For single-page applications (React, Vue, Angular), use a Next.js API route, Express middleware, or a serverless function (Cloudflare Workers, Vercel Edge Functions) as the proxy layer. This also gives you a natural place to add caching, rate limiting for your own users, and request logging.

// Next.js API Route (app/api/search/route.ts)
// The API key stays on the server -- never sent to the browser

export async function POST(request: Request) {
  const body = await request.json();

  const response = await fetch(
    "https://api.moonlit.ai/v1.1/search/hybrid_search",
    {
      method: "POST",
      headers: {
        "Ocp-Apim-Subscription-Key": process.env.MOONLIT_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
    },
  );

  const data = await response.json();
  return Response.json(data);
}