Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 97 additions & 87 deletions content/en/api-reference/account-keys.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
description: "SharpAPI API key management — create, list, rotate, and delete API keys programmatically. Manage multiple keys per account with granular permissions and usage tracking."
description: "SharpAPI API key management — create, list, rotate, and delete API keys programmatically. Responses use the key-management success envelope."
---

import { Callout, Tabs } from 'nextra/components'
Expand All @@ -10,7 +10,7 @@ Create, list, rotate, and delete API keys for your account.

## Authentication

All key management endpoints require authentication via an existing API key. **Available to all tiers.**
Key management endpoints support dashboard session auth and API-key auth. When you authenticate with an API key, that key must be associated with a SharpAPI user account; internal or service keys without `user_id` return `400 validation_error`.

<Callout type="warning">
**Security:** API key management operations are sensitive. Only perform these from server-side code, never from a client-side application.
Expand Down Expand Up @@ -41,10 +41,11 @@ const response = await fetch(
'https://api.sharpapi.io/api/v1/account/keys',
{ headers: { 'X-API-Key': 'YOUR_API_KEY' } }
);
const { data } = await response.json();
const { data, meta } = await response.json();

console.log(`${meta.count}/${meta.max_keys} key slots used`);
for (const key of data) {
console.log(`${key.name}: ${key.id_masked} (${key.is_active})`);
console.log(`${key.name ?? 'Unnamed'}: ${key.id_masked} (${key.is_active})`);
}
```
</Tabs.Tab>
Expand All @@ -56,10 +57,11 @@ response = requests.get(
'https://api.sharpapi.io/api/v1/account/keys',
headers={'X-API-Key': 'YOUR_API_KEY'}
)
keys = response.json()['data']
body = response.json()

for key in keys:
print(f"{key['name']}: {key['id_masked']} ({key['is_active']})")
print(f"{body['meta']['count']}/{body['meta']['max_keys']} key slots used")
for key in body['data']:
print(f"{key['name'] or 'Unnamed'}: {key['id_masked']} ({key['is_active']})")
```
</Tabs.Tab>
</Tabs>
Expand All @@ -68,51 +70,38 @@ for key in keys:

```json
{
"success": true,
"data": [
{
"id": "key_abc123def456",
"id_masked": "sharpapi_...f456",
"id_masked": "...23def456",
"name": "Production",
"tier": "pro",
"is_active": true,
"created_at": "2025-10-15T08:30:00Z",
"updated_at": "2026-02-08T14:22:10Z"
},
{
"id": "key_xyz789ghi012",
"id_masked": "sharpapi_...i012",
"name": "Staging",
"tier": "pro",
"is_active": true,
"created_at": "2026-01-05T12:00:00Z",
"updated_at": "2026-02-07T09:15:30Z"
}
],
"meta": {
"count": 2,
"total": 2,
"pagination": {
"limit": 50,
"offset": 0,
"has_more": false,
"next_offset": null
},
"updated_at": "2026-02-08T14:55:00Z"
"count": 1,
"max_keys": 1
}
}
```

`meta.max_keys` reflects your tier's key limit: Free/Hobby/Pro = 1, Sharp = 2, Enterprise = custom.

### Key Object Fields

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Unique key identifier |
| `id_masked` | string | Masked preview of the key (first and last characters visible) |
| `name` | string \| null | Human-readable key name |
| `tier` | string | Subscription tier associated with the key |
| `is_active` | boolean | Whether the key is currently active |
| `created_at` | string | ISO 8601 timestamp of key creation |
| `updated_at` | string | ISO 8601 timestamp of last key update |
| `id` | string | Unique key identifier. |
| `id_masked` | string | Masked preview of the key id (`...` plus the last 8 characters). |
| `name` | string \| null | Human-readable key name. |
| `tier` | string | Subscription tier associated with the key. |
| `is_active` | boolean | Whether the key is currently active. |
| `created_at` | string | ISO 8601 timestamp of key creation. |
| `updated_at` | string | ISO 8601 timestamp of last key update. |

---

Expand All @@ -128,7 +117,7 @@ POST /api/v1/account/keys

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Yes | A descriptive name for the key (e.g., "Production", "Mobile App") |
| `name` | string | No | Descriptive name for the key, max 100 characters. If omitted, the API assigns a default name. |

### Example Request

Expand All @@ -154,9 +143,9 @@ const response = await fetch(
body: JSON.stringify({ name: 'Mobile App' })
}
);
const { data } = await response.json();
const { data, meta } = await response.json();
console.log(`New key: ${data.key}`);
// IMPORTANT: Store this key securely. It will not be shown again.
console.warn(meta.warning);
```
</Tabs.Tab>
<Tabs.Tab>
Expand All @@ -168,35 +157,39 @@ response = requests.post(
json={'name': 'Mobile App'},
headers={'X-API-Key': 'YOUR_API_KEY'}
)
new_key = response.json()['data']
print(f"New key: {new_key['key']}")
# IMPORTANT: Store this key securely. It will not be shown again.
body = response.json()
print(f"New key: {body['data']['key']}")
print(body['meta']['warning'])
```
</Tabs.Tab>
</Tabs>

### Response (201)
### Response (200)

```json
{
"success": true,
"data": {
"id": "key_new345mno678",
"key": "sharpapi_new345mno678pqr901stu234vwx567",
"key": "sk_live_new345mno678...",
"name": "Mobile App",
"tier": "pro"
},
"meta": {
"warning": "This is the only time the key value will be shown. Store it securely."
}
}
```

<Callout type="warning">
**Important:** The full `key` value is **only returned once** at creation time. Store it securely immediately. Subsequent requests will only show the `id_masked` preview.
**Important:** The full `key` value is only returned once at creation time. Store it securely immediately.
</Callout>

---

## Delete API Key

Permanently revoke and delete an API key.
Permanently revoke an API key.

```
DELETE /api/v1/account/keys/{keyId}
Expand All @@ -206,7 +199,7 @@ DELETE /api/v1/account/keys/{keyId}

| Parameter | Type | Description |
|-----------|------|-------------|
| `keyId` | string | The `key_id` of the key to delete |
| `keyId` | string | The key id to delete. |

### Example Request

Expand All @@ -226,8 +219,8 @@ const response = await fetch(
headers: { 'X-API-Key': 'YOUR_API_KEY' }
}
);
const result = await response.json();
console.log(`Deleted key: ${result.key_id}`);
const { data } = await response.json();
console.log(`Deleted key: ${data.key_id}`);
```
</Tabs.Tab>
<Tabs.Tab>
Expand All @@ -238,8 +231,8 @@ response = requests.delete(
'https://api.sharpapi.io/api/v1/account/keys/key_xyz789ghi012',
headers={'X-API-Key': 'YOUR_API_KEY'}
)
result = response.json()
print(f"Deleted key: {result['key_id']}")
data = response.json()['data']
print(f"Deleted key: {data['key_id']}")
```
</Tabs.Tab>
</Tabs>
Expand All @@ -248,9 +241,12 @@ print(f"Deleted key: {result['key_id']}")

```json
{
"deleted": true,
"key_id": "key_xyz789ghi012",
"message": "API key revoked successfully"
"success": true,
"data": {
"deleted": true,
"key_id": "key_xyz789ghi012",
"message": "API key revoked successfully"
}
}
```

Expand All @@ -265,8 +261,7 @@ print(f"Deleted key: {result['key_id']}")
{
"error": {
"code": "not_found",
"message": "API key not found",
"docs": "https://docs.sharpapi.io/en/api-reference/account-keys"
"message": "Key not found or not owned by you"
}
}
```
Expand All @@ -276,8 +271,7 @@ print(f"Deleted key: {result['key_id']}")
{
"error": {
"code": "validation_error",
"message": "Cannot delete the API key used to authenticate this request",
"docs": "https://docs.sharpapi.io/en/api-reference/account-keys"
"message": "Cannot delete the API key you are currently using"
}
}
```
Expand All @@ -286,7 +280,7 @@ print(f"Deleted key: {result['key_id']}")

## Rotate API Key

Generate a new key value for an existing API key. The old key is immediately revoked and replaced with a new one.
Generate a new key value and replace an existing API key. By default, the old key is revoked immediately. You may request a grace period up to 72 hours.

```
POST /api/v1/account/keys/{keyId}/rotate
Expand All @@ -296,15 +290,24 @@ POST /api/v1/account/keys/{keyId}/rotate

| Parameter | Type | Description |
|-----------|------|-------------|
| `keyId` | string | The `key_id` of the key to rotate |
| `keyId` | string | The key id to rotate. |

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `gracePeriodHours` | number | No | Keep the old key valid for this many hours, from `0` to `72`. Defaults to `0`. |
| `name` | string | No | Name for the replacement key. Defaults to the old key's name. |

### Example Request

<Tabs items={['cURL', 'JavaScript', 'Python']}>
<Tabs.Tab>
```bash
curl -X POST "https://api.sharpapi.io/api/v1/account/keys/key_abc123def456/rotate" \
-H "X-API-Key: YOUR_API_KEY"
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"gracePeriodHours": 0}'
```
</Tabs.Tab>
<Tabs.Tab>
Expand All @@ -313,12 +316,16 @@ const response = await fetch(
'https://api.sharpapi.io/api/v1/account/keys/key_abc123def456/rotate',
{
method: 'POST',
headers: { 'X-API-Key': 'YOUR_API_KEY' }
headers: {
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({ gracePeriodHours: 0 })
}
);
const { data } = await response.json();
console.log(`New key value: ${data.key}`);
// IMPORTANT: Update your application configuration with the new key immediately.
const { data, meta } = await response.json();
console.log(`New key value: ${data.new_key.key}`);
console.warn(meta.warning);
```
</Tabs.Tab>
<Tabs.Tab>
Expand All @@ -327,11 +334,12 @@ import requests

response = requests.post(
'https://api.sharpapi.io/api/v1/account/keys/key_abc123def456/rotate',
json={'gracePeriodHours': 0},
headers={'X-API-Key': 'YOUR_API_KEY'}
)
rotated = response.json()['data']
print(f"New key: {rotated['key']}")
# IMPORTANT: Update your application configuration with the new key immediately.
body = response.json()
print(f"New key: {body['data']['new_key']['key']}")
print(body['meta']['message'])
```
</Tabs.Tab>
</Tabs>
Expand All @@ -340,51 +348,53 @@ print(f"New key: {rotated['key']}")

```json
{
"success": true,
"data": {
"key_id": "key_abc123def456",
"name": "Production",
"key": "sharpapi_rotated789abc012def345ghi678jkl",
"key_preview": "sharpapi_...jkl",
"status": "active",
"rotated_at": "2026-02-08T15:10:00Z",
"previous_key_revoked": true
"new_key": {
"id": "key_new789abc012",
"key": "sk_live_rotated789abc012...",
"name": "Production",
"tier": "pro"
},
"old_key": {
"id": "key_abc123def456",
"revoked": true,
"expires_at": null
}
},
"meta": {
"updated_at": "2026-02-08T15:10:00Z"
"warning": "The new key value is only shown once. Store it securely.",
"message": "Key rotated. Old key has been revoked immediately."
}
}
```

<Callout type="warning">
**Immediate effect:** The previous key value is revoked the instant rotation completes. Update your application configuration before the next API request. The new `key` value is only shown once.
</Callout>

<Callout type="info">
**Tip:** If you are rotating the key you are currently authenticating with, the rotation will succeed, but you must use the new key for all subsequent requests.
**Immediate effect:** Unless you request a grace period, the previous key value is revoked the instant rotation completes. Update your application configuration before the next API request. The new `key` value is only shown once.
</Callout>

---

## Response Headers

All key management endpoints return standard rate limit headers:
Key management endpoints return standard rate-limit headers:

```
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 294
X-RateLimit-Reset: 1707401400
X-RateLimit-Reset: 1782851940
X-Data-Delay: 0
X-Request-Id: req_keys123xyz
X-Request-Id: 1782851943426721-511042
```

## Best Practices

1. **Use descriptive names** - Name keys by their purpose (e.g., "Production Server", "Staging", "Mobile App") to easily identify them later
2. **Rotate regularly** - Rotate keys periodically (e.g., every 90 days) as a security best practice
3. **Use separate keys per environment** - Create distinct keys for production, staging, and development
4. **Review inactive keys** - Identify and clean up unused keys
5. **Store keys in secrets management** - Use environment variables or a secrets manager, never hardcode keys
6. **Revoke compromised keys immediately** - If a key is exposed, delete or rotate it right away
1. **Use descriptive names** - Name keys by their purpose, such as "Production Server" or "Staging".
2. **Rotate regularly** - Rotate keys periodically, such as every 90 days.
3. **Use separate keys per environment** - Create distinct keys for production, staging, and development.
4. **Review inactive keys** - Identify and clean up unused keys.
5. **Store keys in secrets management** - Use environment variables or a secrets manager, never hardcode keys.
6. **Revoke compromised keys immediately** - If a key is exposed, delete or rotate it right away.

## Related Endpoints

Expand Down
Loading