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

# API Keys

> Authenticate with the Turtle Earn API using publishable and secret keys.

Every Earn API endpoint requires an API key, passed on the `X-API-Key` header. Use the key issued when your organization was created, or contact the Turtle team if you need one. Store it securely.

## Key types

There are two key types, distinguished by prefix.

The publishable key (`pk_live_`) is safe in client-side and browser code. It covers read access to opportunities, deposit and wallet activity, and the verify endpoint.

The secret key (`sk_live_`) is server-side only. It is required for write operations such as generating deposit and withdrawal transactions, creating memberships, and managing streams.

<Warning>
  Never expose your `sk_live_` key in client-side code, browsers, or public repositories. Treat it like a database password.
</Warning>

### Publishable key

<CodeGroup>
  ```bash curl theme={null}
  curl https://earn.turtle.xyz/v2/opportunities/ \
    -H "X-API-Key: pk_live_xxxxx"
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch('https://earn.turtle.xyz/v2/opportunities/', {
    headers: { 'X-API-Key': 'pk_live_xxxxx' },
  });
  ```
</CodeGroup>

### Secret key

Use this from your backend only. The example below builds a deposit, a write action that the publishable key cannot perform. See [Deposit](/sdk/earn/deposit) for the full request and response.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "https://earn.turtle.xyz/v2/actions/deposit/{opportunityId}" \
    -H "X-API-Key: sk_live_xxxxx" \
    -H "Content-Type: application/json" \
    -d '{ "userAddress": "0x1234...", "tokenIn": "0xA0b8...", "amount": "1000000", "distributorId": "your-distributor-id", "mode": "direct" }'
  ```

  ```typescript Node.js theme={null}
  const res = await fetch(
    `https://earn.turtle.xyz/v2/actions/deposit/${opportunityId}`,
    {
      method: 'POST',
      headers: {
        'X-API-Key': process.env.TURTLE_SECRET_KEY!,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        userAddress: '0x1234...',
        tokenIn: '0xA0b8...',
        amount: '1000000',
        distributorId: 'your-distributor-id',
        mode: 'direct',
      }),
    }
  );
  ```
</CodeGroup>

## Rate limits

Rate limit state is returned in response headers. The budget is per key and resets hourly.

| Header                  | Description                          |
| ----------------------- | ------------------------------------ |
| `X-RateLimit-Limit`     | Hourly cost unit budget              |
| `X-RateLimit-Remaining` | Cost units remaining this hour       |
| `X-RateLimit-Used`      | Cost units consumed this hour        |
| `X-Monthly-Limit`       | Monthly cost unit cap, if configured |
| `X-Monthly-Usage`       | Cost units consumed this month       |
| `X-Monthly-Remaining`   | Cost units remaining this month      |

When a limit is exceeded, the API returns `429 Too Many Requests`.

## Next steps

A valid key is only half of access control. Action endpoints also require the user's wallet to be a registered Turtle member. See [Register Wallet](/sdk/authentication/register-wallet) for the membership flow.
