Skip to content

Authentication

Zelta Pay API uses API key-based authentication for all public endpoints. This guide covers how to obtain, use, and manage your API keys securely.

All API requests must include your API key in the X-API-Key header:

X-API-Key: your-api-key-here

The server validates this key before processing your request. Each account can have multiple API keys for different applications or environments.

  1. Log in to your Zelta Pay Dashboard
  2. Navigate to Settings → API Keys
  3. Click Create New API Key
  4. Give it a descriptive name (e.g., “Production App”, “Mobile App”)
  5. Copy the generated key and store it securely
  6. Click Save

Security

Never commit API keys to version control. Use environment variables or secure secret management systems instead.

  • Limit: 60 requests per minute per API key
  • Window: 1 minute rolling window
  • Per-key: Each API key has its own independent limit

Every response includes rate limit information in headers:

HeaderDescriptionExample
RateLimit-LimitMaximum requests allowed60
RateLimit-RemainingRequests left in window45
RateLimit-ResetUnix timestamp of window reset1640995200
HTTP/1.1 200 OK
RateLimit-Limit: 60
RateLimit-Remaining: 59
RateLimit-Reset: 1640995260
Content-Type: application/json

When you exceed the rate limit, you’ll receive a 429 Too Many Requests response:

{
"success": false,
"message": "Too Many Requests",
"error": {
"code": "ERR_RATE_LIMIT_EXCEEDED"
},
"timestamp": "2024-01-15T12:00:00.000Z"
}

The RateLimit-Reset header indicates when the window resets (in Unix seconds).

  1. Monitor rate limit headers in your responses
  2. Implement exponential backoff for retries
  3. Use multiple API keys for high-volume applications
  4. Cache responses when possible to reduce API calls
CodeHTTPDescriptionSolution
ERR_MISSING_API_KEY401X-API-Key header is missingAdd the X-API-Key header to your request
ERR_API_KEY_NOT_FOUND404API key not found or invalidVerify your API key is correct
ERR_ACCOUNT_SUSPENDED403Account is suspendedContact support@zelta.dev
ERR_RATE_LIMIT_EXCEEDED429Rate limit exceeded (60 requests/min)Implement backoff/retry logic
{
"success": false,
"message": "API key not found",
"error": {
"code": "ERR_API_KEY_NOT_FOUND"
},
"timestamp": "2024-01-15T12:00:00.000Z"
}
const apiKey = process.env.ZELTA_API_KEY;
async function makeApiRequest(endpoint, options = {}) {
const response = await fetch(`https://api-pay.zelta.dev${endpoint}`, {
...options,
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
return response.json();
}
// Usage
try {
const paymentLinks = await makeApiRequest('/v1/payment-links');
console.log(paymentLinks);
} catch (error) {
console.error('API Error:', error.message);
}
export default {
async fetch(request, env, ctx) {
const apiKey = env.ZELTA_API_KEY;
async function makeApiRequest(endpoint, options = {}) {
const response = await fetch(`https://api-pay.zelta.dev${endpoint}`, {
...options,
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
return response.json();
}
// Example usage
try {
const paymentLinks = await makeApiRequest('/v1/payment-links');
return new Response(JSON.stringify(paymentLinks), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
}
};
import { Hono } from 'hono';
import { cors } from 'hono/cors';
type Bindings = {
ZELTA_API_KEY: string;
};
const app = new Hono<{ Bindings: Bindings }>();
// Enable CORS
app.use('*', cors());
async function makeApiRequest(endpoint: string, apiKey: string, options: RequestInit = {}) {
const response = await fetch(`https://api-pay.zelta.dev${endpoint}`, {
...options,
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
return response.json();
}
// Example endpoint
app.get('/payment-links', async (c) => {
try {
const paymentLinks = await makeApiRequest('/v1/payment-links', c.env.ZELTA_API_KEY);
return c.json(paymentLinks);
} catch (error) {
return c.json({ error: error.message }, 500);
}
});
export default app;
import requests
import os
api_key = os.getenv('ZELTA_API_KEY')
def make_api_request(endpoint, **kwargs):
headers = {
'X-API-Key': api_key,
'Content-Type': 'application/json'
}
headers.update(kwargs.get('headers', {}))
response = requests.request(
method=kwargs.get('method', 'GET'),
url=f'https://api-pay.zelta.dev{endpoint}',
headers=headers,
**{k: v for k, v in kwargs.items() if k not in ['method', 'headers']}
)
if not response.ok:
error = response.json()
raise Exception(error['message'])
return response.json()
# Usage
try:
payment_links = make_api_request('/v1/payment-links')
print(payment_links)
except Exception as error:
print(f'API Error: {error}')
<?php
$apiKey = $_ENV['ZELTA_API_KEY'];
function makeApiRequest($endpoint, $options = []) {
global $apiKey;
$headers = [
'X-API-Key: ' . $apiKey,
'Content-Type: application/json'
];
if (isset($options['headers'])) {
$headers = array_merge($headers, $options['headers']);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api-pay.zelta.dev' . $endpoint);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if (isset($options['method'])) {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $options['method']);
}
if (isset($options['data'])) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($options['data']));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 400) {
$error = json_decode($response, true);
throw new Exception($error['message']);
}
return json_decode($response, true);
}
// Usage
try {
$paymentLinks = makeApiRequest('/v1/payment-links');
print_r($paymentLinks);
} catch (Exception $e) {
echo 'API Error: ' . $e->getMessage();
}
?>

Store API keys in environment variables:

Terminal window
# .env file
ZELTA_API_KEY=your-api-key-here

Regularly rotate your API keys:

  1. Create a new API key
  2. Update your applications
  3. Delete the old key
  • Use different API keys for different environments
  • Limit key permissions when possible
  • Monitor key usage in your dashboard

Always handle authentication errors gracefully:

async function handleApiError(error) {
if (error.message.includes('API key not found')) {
console.error('Invalid API key');
// Redirect to settings or show error
} else if (error.message.includes('Too Many Requests')) {
console.error('Rate limit exceeded');
// Implement backoff strategy
} else {
console.error('API Error:', error.message);
}
}
Terminal window
curl -X GET "https://api-pay.zelta.dev/v1/payment-links" \
-H "X-API-Key: your-api-key-here" \
-H "Content-Type: application/json"
{
"success": true,
"data": {
"paymentLinks": [],
"total": 0
},
"message": null,
"timestamp": "2024-01-15T12:00:00.000Z"
}

401 Unauthorized

  • Check API key is correct
  • Ensure X-API-Key header is included
  • Verify key is active in dashboard

403 Forbidden

  • Account may be suspended
  • Contact support for assistance

429 Too Many Requests

  • Implement rate limiting in your application
  • Use multiple API keys for high volume
  • Cache responses when possible

Now that you understand authentication, explore these guides: