> ## Documentation Index
> Fetch the complete documentation index at: https://docs.privy.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Optimize your setup

For developers looking to optimize their Privy integration, we have a few key features that should help fine-tune the performance your setup.

## Manually set a verification key for authorization

When verifying a Privy access token to authorize requests to your servers, by default the Privy Client's `verifyAuthToken` method will make a request to Privy's API to fetch the verification key for your app. Although it is cached for reuse, you can avoid this API request entirely by copying your verification key from the [Configuration > App settings > Basics tab of the Dashboard](https://dashboard.privy.io/apps?page=settings), under "Verify with key instead":

<CodeGroup>
  ```ts @privy-io/node theme={"system"}
  import {PrivyClient} from '@privy-io/node';

  const privy = new PrivyClient({
    appId: 'your-privy-app-id',
    appSecret: 'your-privy-app-secret',
    // Set the verification key from the Dashboard when initializing the PrivyClient
    jwtVerificationKey: 'paste-your-verification-key-from-the-dashboard'
  });
  ```
</CodeGroup>

If you ever rotate your verification key, you will have to update this, but this will remove any network dependency on Privy for token verification.

## Get user data with identity tokens

If you need access to the user object, especially on the server, this can be a costly action. To remove a network call from your critical path, we recommend using Privy's [identity tokens](/user-management/users/identity-tokens), which include the latest user information in token form. While it does not have the full user details (it omits certain lesser-needed fields for efficiency), it should have what you need to get started quickly.

## Set a custom API URL for `HttpOnly` cookies (`react-auth` only)

In the case where you have set up and enabled `HttpOnly` cookies, on initial page load, the Privy SDK will start by making a call to fetch app details on our default `https://auth.privy.io` API URL. In `HttpOnly` cookie mode however, all your requests are routed through `https://privy.<customdomain.com>`. To avoid an occasional extra call on page load, we recommend explicitly setting the `apiUrl` in your `PrivyProvider`:

```tsx theme={"system"}
return (
  <PrivyProvider
    appId={'your-app-ID'}
    // @ts-expect-error currently a beta feature
    apiUrl="https://privy.customdomain.com"
  >
    {children}
  </PrivyProvider>
);
```

Note that this has a risk - if you are ever *disabling* `HttpOnly` cookies, you will need to remove this in order for your app to continue functioning properly. For a smooth transition, first remove the `apiUrl`, deploy, and then disable HttpOnly cookies.

## Handling rate limits

When your application encounters rate limiting (HTTP 429 responses), implementing proper retry logic ensures a smooth user experience and optimal API usage.

### Understanding rate limit responses

When you exceed a rate limit, Privy's API returns a `429 Too Many Requests` status code. Rate limits are applied per endpoint and are designed to ensure fair usage across all applications.

### Best practices for handling rate limits

#### 1. Implement exponential backoff

Exponential backoff is a standard error-handling strategy that gradually increases the wait time between retry attempts:

```typescript theme={"system"}
export {};
declare const privy: any;

async function makeRequestWithRetry(
  requestFn: () => Promise<any>,
  maxRetries: number = 5,
  baseDelay: number = 1000
): Promise<any> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await requestFn();
    } catch (error: any) {
      // Check if it's a rate limit error
      if (error.status === 429 && attempt < maxRetries - 1) {
        // Calculate exponential backoff delay: 1s, 2s, 4s, 8s, 16s
        const delay = baseDelay * Math.pow(2, attempt);

        // Add jitter to prevent thundering herd
        const jitter = Math.random() * 1000;

        console.log(`Rate limited. Retrying in ${delay + jitter}ms...`);
        await new Promise((resolve) => setTimeout(resolve, delay + jitter));
      } else {
        throw error;
      }
    }
  }
  throw new Error('Max retries exceeded');
}

// Usage example
const user = await makeRequestWithRetry(() => privy.users()._get('did:privy:xxxxx'));
```

#### 2. Batch your requests

Instead of making individual API calls for each operation, batch multiple operations together when possible:

```typescript theme={"system"}
export {};
declare const privy: any;
const userIds = ['did:privy:xxxxx'];
const processUser = (_user: unknown) => {};

// ❌ Avoid: Multiple individual requests
for (const userId of userIds) {
  const user = await privy.users()._get(userId);
  processUser(user);
}

// ✅ Better: Use list endpoint with pagination
for await (const user of privy.users().list()) {
  if (userIds.includes(user.id)) {
    processUser(user);
  }
}
```

For bulk user operations, use the [batch user creation endpoint](/user-management/migrating-users-to-privy/create-or-import-a-batch-of-users) which allows creating up to 100 users per request.

#### 3. Cache responses when appropriate

For data that doesn't change frequently, implement caching to reduce API calls:

```typescript theme={"system"}
declare const privy: any;

const userCache = new Map<string, {user: any; timestamp: number}>();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

async function getCachedUser(userId: string) {
  const cached = userCache.get(userId);

  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.user;
  }

  const user = await privy.users()._get(userId);
  userCache.set(userId, {user, timestamp: Date.now()});

  return user;
}
```

#### 4. Use identity tokens for authenticated users

For getting user data about authenticated users, use [identity tokens](/user-management/users/identity-tokens) instead of making API calls. This approach is rate-limit-free and provides user information directly from the token.

```typescript theme={"system"}
export {};
declare const privy: any;
const idToken = 'insert-id-token';
const userId = 'did:privy:xxxxx';

// ✅ Preferred: Use identity token (no API call)
const userFromToken = await privy.users().get({id_token: idToken});

// ❌ Avoid when possible: Direct API call (subject to rate limits)
const userFromApi = await privy.users()._get(userId);
```

#### 5. Implement circuit breakers

For production applications, consider implementing a circuit breaker pattern to temporarily stop making requests when rate limits are consistently hit:

```typescript theme={"system"}
class CircuitBreaker {
  private failureCount = 0;
  private lastFailureTime = 0;
  private readonly threshold = 3;
  private readonly cooldown = 60000; // 1 minute

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    // Check if circuit is open
    if (this.failureCount >= this.threshold) {
      const timeSinceLastFailure = Date.now() - this.lastFailureTime;
      if (timeSinceLastFailure < this.cooldown) {
        throw new Error('Circuit breaker is open. Too many rate limit errors.');
      }
      // Reset after cooldown
      this.failureCount = 0;
    }

    try {
      const result = await fn();
      this.failureCount = 0; // Reset on success
      return result;
    } catch (error: any) {
      if (error.status === 429) {
        this.failureCount++;
        this.lastFailureTime = Date.now();
      }
      throw error;
    }
  }
}
```

### Additional optimization tips

* **Monitor your usage**: Track your API call patterns to identify optimization opportunities
* **Use webhooks**: For real-time updates, consider using webhooks instead of polling endpoints
* **Optimize query patterns**: Review your query logic to eliminate unnecessary or redundant API calls
* **Parallelize independent requests**: Use `Promise.all()` for independent requests to reduce total execution time while staying within rate limits

By following these practices, your application can handle rate limits gracefully and provide a reliable experience for your users.
