Pondral

Authentication

All API requests require authentication using an API key. API keys provide secure access to the Pondral AI Visibility platform and can be scoped to specific permissions.

Getting Your API Key

  1. Log in to your Pondral dashboard
  2. Open Settings and find the API keys card
  3. Give the key a descriptive name (e.g. "Production server")
  4. Select the scopes your application needs
  5. Click "Create new key"
  6. Copy and save the key immediately — it is not shown again

Keys are available on the Growth plan and above, and stop working one year after they are created. The listing in Settings shows each key's expiry date.

Making Authenticated Requests

Include your API key in the Authorization header with the Bearer scheme:

curl -X GET https://pondral.com/api/v1/projects \
  -H "Authorization: Bearer YOUR_API_KEY"

JavaScript / TypeScript

const response = await fetch(
  'https://pondral.com/api/v1/projects',
  {
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    }
  }
);
const data = await response.json();

API Key Scopes

Scopes limit what an API key can do. Create keys with only the scopes you need for better security.

ScopeDescription
readRead-only access to every GET endpoint. Grants nothing that creates, spends or deletes.
analyzeRun AI visibility analyses
projectsCreate and list projects
resultsRead analysis results
reportsAccess generated reports
monitoringManage monitoring schedules
webhooksCreate and manage webhooks
usageView usage statistics

Security Best Practices

  • Never commit API keys to version control
  • Use environment variables to store API keys securely
  • Use separate API keys for different environments (dev, staging, prod)
  • Rotate API keys regularly
  • Revoke keys immediately if they are compromised
  • Only request the minimum scopes needed for your use case
  • Verify webhook signatures using the provided secret

Rate Limits

API requests are rate-limited based on your subscription tier. Limits are reset every 60 seconds. The remaining requests in the current window are returned in the X-RateLimit-Remaining header.

The API is available on the Growth plan and above. A call made with a key on any other plan returns 403 PLAN_REQUIRED, so the Free and SMB limits that used to be listed here described requests that could never succeed.

Growth

300 req/min

Agency

1,000 req/min

Error Codes & Status Codes

CodeHTTP StatusMeaning
INVALID_API_KEY401API key is missing, invalid, or expired
INSUFFICIENT_SCOPE403API key does not have permission for this endpoint
RATE_LIMIT_EXCEEDED429Too many requests in the time window
VALIDATION_ERROR400Invalid request parameters or body
NOT_FOUND404The requested resource does not exist
BILLING_LIMIT_EXCEEDED403Monthly usage limit reached for this tier
ENGINE_FAILURE502AI engine error (try again or use different engine)
ENGINE_NOT_ALLOWED403Your plan does not include the requested engine
BILLING_UNAVAILABLE503Plan or quota lookup failed (transient, retry)
INTERNAL_ERROR500Server error (try again or contact support)

Webhook Security

Every delivery is signed with HMAC-SHA256 using your webhook secret. The signed material is the X-Webhook-Timestamp header value, a period, then the raw request body — so a captured delivery cannot be replayed later with the signature still verifying. Verify the signature, then reject deliveries whose timestamp is more than five minutes old.

Verifying Signatures (Node.js)

import crypto from 'crypto';

export function verifyWebhookSignature(
  payload: string,
  timestamp: string,
  signature: string,
  secret: string
): boolean {
  const hmac = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${payload}`)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(hmac),
    Buffer.from(signature)
  );
}

// In your webhook handler:
const signature = req.headers['x-webhook-signature'];
const timestamp = req.headers['x-webhook-timestamp'];
const payload = req.body.toString();

if (!verifyWebhookSignature(payload, timestamp, signature, webhookSecret)) {
  return res.status(401).json({ error: 'Invalid signature' });
}

// Replay window: reject deliveries older than 5 minutes.
if (Math.abs(Date.now() - Date.parse(timestamp)) > 5 * 60 * 1000) {
  return res.status(401).json({ error: 'Stale delivery' });
}

// Process webhook...

Every delivery carries four headers: X-Webhook-Signature, X-Webhook-Timestamp, X-Webhook-Event and X-Webhook-Id. Always verify the signature before processing an event.

For additional support or questions, visit our documentation or contact support.

Last updated: August 20, 2026