Authentication
Builder endpoints are protected by an API key, and state-changing endpoints additionally require an HMAC-SHA256 request signature.
Headers
| Header | Required on | Description |
|---|---|---|
X-Api-Key |
All builder endpoints | Your builder API key (bld_...) |
X-Timestamp |
/v1/submit, /v1/trades |
Current Unix timestamp (seconds) |
X-Signature |
/v1/submit, /v1/trades |
HMAC-SHA256 signature (hex) |
Endpoints that only require X-Api-Key: /v1/keys, /v1/sign, /v1/deposit/address. See the API Reference for each endpoint's exact requirements.
Signing
The signature is computed over: timestamp_string + request_body
import hmac, hashlib, time, json, requests
API_KEY = "bld_a1b2c3..."
API_SECRET = "deadbeef1234..." # hex-encoded 32 bytes
SIGNER_URL = "https://signer.parti-oracle.pbcapps.dev"
def signed_request(method, path, body=None):
timestamp = str(int(time.time()))
body_str = json.dumps(body) if body else ""
# HMAC over: timestamp + body
message = timestamp + body_str
signature = hmac.new(
bytes.fromhex(API_SECRET),
message.encode(),
hashlib.sha256
).hexdigest()
headers = {
"X-Api-Key": API_KEY,
"X-Timestamp": timestamp,
"X-Signature": signature,
"Content-Type": "application/json",
}
return requests.request(method, SIGNER_URL + path,
headers=headers, json=body)
const crypto = require('crypto');
function signedFetch(path, body) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const bodyStr = JSON.stringify(body);
const message = timestamp + bodyStr;
const signature = crypto
.createHmac('sha256', Buffer.from(API_SECRET, 'hex'))
.update(message)
.digest('hex');
return fetch(SIGNER_URL + path, {
method: 'POST',
headers: {
'X-Api-Key': API_KEY,
'X-Timestamp': timestamp,
'X-Signature': signature,
'Content-Type': 'application/json',
},
body: bodyStr,
});
}
Replay Protection
Requests are rejected if the timestamp is more than 5 seconds old (or 5 seconds in the future). Keep your server clock synchronized — NTP is strongly recommended.
Rate Limiting
Builder signing endpoints are rate-limited with a per-key token bucket.
- Keying:
X-Api-Keywhen present, falling back to the client IP. - Default: 10 requests/second per key, with a burst capacity of 10.
- Tunable: operators can change the limit via the
RATE_LIMIT_RPSenvironment variable on the signer. - Response:
HTTP 429 Too Many Requestswith body{"error": "rate limit exceeded"}when the bucket is empty. - Admin endpoints (
/v1/admin/*) and/healthbypass the limiter.
If you need a higher limit for production traffic, contact the operator.
Security Notes
- Never expose your
API_SECRETin client-side code - All signing should happen on your backend
- The HMAC prevents request tampering — any change to the body invalidates the signature
- Use HTTPS for all requests