Skip to main content

API Tokens

Create and manage personal API tokens for programmatic access to the Sealmetrics API.

Overview​

API tokens provide:

  • Long-lived authentication for integrations
  • Scoped permissions per token
  • Optional account restrictions
  • Automatic expiration

Base path: /api-tokens


Scopes​

API tokens can be granted the following permissions:

ScopeDescription
stats:readRead analytics data (traffic, conversions, pages)
sites:readRead site configuration (domains, settings, channel rules)
accounts:readRead account information
channel_rules:writeChannel rules: create and edit drafts only. Nothing on live rules
channel_rules:publishChannel rules: create live rules and edit/delete live ones. Implies channel_rules:write

Note: API tokens are read-only apart from channel rules, which are the single exception. Every other write operation requires JWT authentication via the dashboard. See Channel Groups for what each channel-rule scope allows, and Channel Grouping for how to create the key.

Permissions are fixed at creation

A token's scopes cannot be edited later. To change them, revoke the token and create a new one. A 403 response naming Required scope: ... means the token lacks that scope.

List Available Scopes​

GET /api-tokens/scopes

Response:

{
"data": {
"scopes": [
{
"id": "stats:read",
"name": "Stats Read",
"description": "Read analytics data"
},
{
"id": "sites:read",
"name": "Sites Read",
"description": "Read site configuration"
},
{
"id": "accounts:read",
"name": "Accounts Read",
"description": "Read account info"
},
{
"id": "channel_rules:write",
"name": "Channel Rules: Drafts",
"description": "Channel rules: create and edit drafts only"
},
{
"id": "channel_rules:publish",
"name": "Channel Rules: Publish",
"description": "Channel rules: create live rules and edit/delete live ones"
}
]
}
}

List Tokens​

GET /api-tokens

Query Parameters:

ParameterTypeDefaultDescription
include_inactivebooleanfalseInclude revoked tokens

Response:

{
"data": {
"tokens": [
{
"id": 1,
"name": "Production API",
"token_prefix": "sm_abc",
"scopes": ["stats:read", "sites:read", "accounts:read"],
"account_ids": ["acme-corp"],
"is_active": true,
"expires_at": "2026-01-10T00:00:00Z",
"last_used_at": "2025-01-10T14:30:00Z",
"created_at": "2025-01-01T10:00:00Z"
}
],
"total": 1
}
}

Create Token​

POST /api-tokens

Request Body:

{
"name": "My Integration",
"scopes": ["stats:read", "sites:read"],
"account_ids": ["acme-corp", "other-site"],
"expires_in_days": 365
}
FieldTypeRequiredDescription
namestringYesToken name (1-100 chars)
scopesstring[]YesPermission scopes
account_idsstring[]NoRestrict to specific accounts
expires_in_daysintNoDays until expiration, 1-365 (default: none — never expires)

Response (201 Created):

{
"data": {
"id": 2,
"name": "My Integration",
"token": "sm_abc123def456ghi789...",
"token_prefix": "sm_abc",
"scopes": ["stats:read", "sites:read"],
"account_ids": ["acme-corp", "other-site"],
"is_active": true,
"expires_at": "2026-01-10T00:00:00Z",
"created_at": "2025-01-10T14:30:00Z"
}
}

Important: The full token value is only returned once. Store it securely.


Get Token​

GET /api-tokens/{token_id}

Response:

{
"data": {
"id": 1,
"name": "Production API",
"token_prefix": "sm_abc",
"scopes": ["stats:read", "sites:read", "accounts:read"],
"account_ids": ["acme-corp"],
"is_active": true,
"expires_at": "2026-01-10T00:00:00Z",
"last_used_at": "2025-01-10T14:30:00Z",
"created_at": "2025-01-01T10:00:00Z"
}
}

Revoke Token​

DELETE /api-tokens/{token_id}

Revokes a token immediately. This action cannot be undone.

Response:

{
"data": {
"id": 1,
"name": "Production API",
"is_active": false,
"revoked_at": "2025-01-10T14:30:00Z"
}
}

Using API Tokens​

Include the token in the X-API-Key header:

curl -X GET "https://my.sealmetrics.com/api/v1/stats/overview?site_id=acme" \
-H "X-API-Key: sm_abc123def456ghi789..."

Token Best Practices​

1. Use Minimal Scopes​

Only grant the scopes your integration needs:

{
"name": "Analytics Only",
"scopes": ["stats:read"]
}

2. Restrict to Specific Sites​

If your integration only needs access to certain sites:

{
"name": "Client Dashboard",
"scopes": ["stats:read"],
"account_ids": ["client-a", "client-b"]
}

3. Set Appropriate Expiration​

For temporary integrations, use short expiration:

{
"name": "Audit Script",
"scopes": ["stats:read"],
"expires_in_days": 7
}

4. Rotate Tokens Regularly​

Create a new token before the old one expires:

# Create new token
new_token = create_token("Production API v2", scopes=["stats:read", "sites:read"])

# Update your application config
update_config(new_token)

# Revoke old token after confirming new one works
revoke_token(old_token_id)

5. Never Commit Tokens​

Store tokens in environment variables:

# .env file (never commit!)
SEALMETRICS_API_KEY=sm_abc123...
import os
API_KEY = os.environ["SEALMETRICS_API_KEY"]

Token Errors​

The error.code is derived from the HTTP status (401 → unauthorized, 403 → forbidden). The specific reason is carried in the human-readable error.message.

HTTP CodeError CodeExample message
401unauthorizedInvalid API key (token not found or revoked)
401unauthorizedAPI key has expired
401unauthorizedInvalid API key format (missing sm_ prefix)
403forbiddenRequired scope: <scope> (token lacks required scope)
403forbiddenAccess denied to account: <id> (account not in token's allowed list)

Code Examples​

Python​

import requests

BASE_URL = "https://my.sealmetrics.com/api/v1"

def create_api_token(auth_token: str, name: str, scopes: list) -> dict:
"""Create a new API token."""
response = requests.post(
f"{BASE_URL}/api-tokens",
headers={"Authorization": f"Bearer {auth_token}"},
json={
"name": name,
"scopes": scopes,
"expires_in_days": 365
}
)
response.raise_for_status()
return response.json()["data"]

def revoke_token(auth_token: str, token_id: int):
"""Revoke an API token."""
response = requests.delete(
f"{BASE_URL}/api-tokens/{token_id}",
headers={"Authorization": f"Bearer {auth_token}"}
)
response.raise_for_status()

JavaScript​

async function createApiToken(authToken, name, scopes) {
const response = await fetch(`${BASE_URL}/api-tokens`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name,
scopes,
expires_in_days: 365
})
});

if (!response.ok) {
throw new Error(`Failed to create token: ${response.status}`);
}

const { data } = await response.json();
return data;
}
Written and maintained by the Sealmetrics Team