Keep Your Private Key Secret
Never expose your private key in client-side JavaScript, mobile apps, or public repositories. All API calls should be made from a secure server-side environment.
API Guide
Never expose your private key in client-side JavaScript, mobile apps, or public repositories. All API calls should be made from a secure server-side environment.
X-Timestamp is more than 5 minutes from the server clock. Make sure your server's clock is synchronized using NTP. If you see REQUEST_EXPIRED errors, check your system time.| Condition | Limit |
|---|---|
With X-Public-Key header | 60 requests/minute per key |
Without X-Public-Key header | 10 requests/minute per IP |
429 Too Many Requests. Implement exponential backoff in your retry logic.path component must include the full URI with query string and a leading /. For example: /api/v1/events?count=5, not /events?count=5 or api/v1/events.Node.js
const crypto = require("crypto");
// Helper: create signed headers for any request
function createSignedHeaders(method, path, body = "") {
const publicKey = process.env.API_PUBLIC_KEY;
const privateKey = process.env.API_PRIVATE_KEY;
const timestamp = Math.floor(Date.now() / 1000).toString();
const signingString = `${timestamp}\n${method}\n${path}\n${body}`;
const signature = crypto
.createHmac("sha256", privateKey)
.update(signingString)
.digest("hex");
return {
"X-Public-Key": publicKey,
"X-Timestamp": timestamp,
"X-Signature": signature,
};
}
// Helper: fetch with retry + backoff
async function apiRequest(method, path, body) {
const url = `https://backend.localbusiness.pro${path}`;
const rawBody = body ? JSON.stringify(body) : "";
const headers = {
...createSignedHeaders(method, path, rawBody),
...(body ? { "Content-Type": "application/json" } : {}),
};
for (let attempt = 0; attempt < 3; attempt++) {
const res = await fetch(url, { method, headers, body: rawBody || undefined });
if (res.status === 429 || res.status >= 500) {
await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
continue;
}
if (!res.ok) {
throw new Error(`API error: ${res.status} ${res.statusText}`);
}
return res.json();
}
throw new Error("Max retries exceeded");
}
// Usage
const events = await apiRequest("GET", "/api/v1/events?count=10");
console.log(events);
Python
import hmac, hashlib, time, os, requests
from functools import wraps
def create_signed_headers(method: str, path: str, body: str = "") -> dict:
"""Create HMAC-signed headers for an API request."""
public_key = os.environ["API_PUBLIC_KEY"]
private_key = os.environ["API_PRIVATE_KEY"]
timestamp = str(int(time.time()))
signing_string = f"{timestamp}\n{method}\n{path}\n{body}"
signature = hmac.new(
private_key.encode(), signing_string.encode(), hashlib.sha256
).hexdigest()
return {
"X-Public-Key": public_key,
"X-Timestamp": timestamp,
"X-Signature": signature,
}
def api_request(method: str, path: str, body: dict | None = None):
"""Make an API request with retry + backoff."""
url = f"https://backend.localbusiness.pro{path}"
raw_body = "" if body is None else __import__("json").dumps(body)
headers = create_signed_headers(method, path, raw_body)
if body:
headers["Content-Type"] = "application/json"
for attempt in range(3):
resp = requests.request(method, url, headers=headers, data=raw_body or None)
if resp.status_code in (429, 500, 502, 503):
time.sleep(2 ** attempt)
continue
resp.raise_for_status()
return resp.json()
raise Exception("Max retries exceeded")
# Usage
events = api_request("GET", "/api/v1/events?count=10")
print(events)
PHP
<?php
function createSignedHeaders(string $method, string $path, string $body = ''): array {
$publicKey = getenv('API_PUBLIC_KEY');
$privateKey = getenv('API_PRIVATE_KEY');
$timestamp = time();
$signingString = $timestamp . "\n" . $method . "\n" . $path . "\n" . $body;
$signature = hash_hmac('sha256', $signingString, $privateKey);
return [
"X-Public-Key: $publicKey",
"X-Timestamp: $timestamp",
"X-Signature: $signature",
];
}
function apiRequest(string $method, string $path, ?array $body = null): array {
$url = 'https://backend.localbusiness.pro' . $path;
$rawBody = $body ? json_encode($body) : '';
$headers = createSignedHeaders($method, $path, $rawBody);
if ($body) {
$headers[] = 'Content-Type: application/json';
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
if ($rawBody) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $rawBody);
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 400) {
throw new Exception("API error: $httpCode");
}
return json_decode($response, true);
}
// Usage
$events = apiRequest('GET', '/api/v1/events?count=10');
print_r($events);
cURL
#!/bin/bash
# Reusable helper function for signed API requests
api_request() {
local METHOD="$1"
local PATH_URI="$2"
local BODY="${3:-}"
TIMESTAMP=$(date +%s)
SIGNATURE=$(printf '%s
%s
%s
%s' "$TIMESTAMP" "$METHOD" "$PATH_URI" "$BODY" \
| openssl dgst -sha256 -hmac "$API_PRIVATE_KEY" | awk '{print $2}')
curl -s -X "$METHOD" \
-H "X-Public-Key: $API_PUBLIC_KEY" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Signature: $SIGNATURE" \
"https://backend.localbusiness.pro$PATH_URI"
}
# Usage
export API_PUBLIC_KEY="your-public-key"
export API_PRIVATE_KEY="your-private-key"
api_request GET /api/v1/me
api_request GET "/api/v1/events?count=10"