Authentication
All endpoints (except GET /v1/config) require authentication via an API key.
API Key Format
fxd_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- Prefix: always
fxd_ - Suffix: 32 random hex characters
- Total length: 36 characters
- Example:
fxd_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4
Authentication Methods
Method 1 — HTTP Header (Recommended)
Pass the key in the X-API-Key header. This is the preferred method for all server-to-server calls.
X-API-Key: fxd_your_key_here
curl -H "X-API-Key: fxd_your_key_here" \
"https://api.pipdata.net/v1/symbols"
Method 2 — Authorization Bearer
Standard Bearer token format, compatible with OAuth tooling.
Authorization: Bearer fxd_your_key_here
curl -H "Authorization: Bearer fxd_your_key_here" \
"https://api.pipdata.net/v1/symbols"
Method 3 — Query Parameter
Pass the key as a URL query parameter. Convenient for browser environments or WebSocket connections where custom headers cannot be set.
GET /v1/symbols?api_key=fxd_your_key_here
WebSocket Authentication
const ws = new WebSocket(
"wss://api.pipdata.net/v1/stream?api_key=fxd_your_key_here"
);
Per-Key Access Controls
Each key can be configured with the following restrictions:
| Restriction | Description |
|---|---|
| Feed permissions | Key only accesses specific feeds (e.g., WMMARKETS:ZERO only) |
| RPM | Maximum requests per minute |
| RPD | Maximum requests per day |
| IP allowlist | If set, only listed IP addresses are accepted |
| Expiry date | Key is automatically deactivated after this date |
| WS connections | Maximum simultaneous WebSocket connections |
| WS symbols | Maximum symbols per WebSocket connection |
Rate-Limit Response Headers
Every API response includes these headers so you can track consumption in real time:
X-RateLimit-Limit-Minute: 120
X-RateLimit-Remaining-Minute: 115
X-RateLimit-Reset-Minute: 1749600060
X-RateLimit-Limit-Day: 50000
X-RateLimit-Remaining-Day: 49885
| Header | Meaning |
|---|---|
X-RateLimit-Limit-Minute | Per-minute request cap |
X-RateLimit-Remaining-Minute | Requests remaining this minute |
X-RateLimit-Reset-Minute | Unix timestamp when the minute window resets |
X-RateLimit-Limit-Day | Daily request cap |
X-RateLimit-Remaining-Day | Requests remaining today |
Authentication Errors
401 — Missing or invalid key
{
"error": {
"code": "UNAUTHORIZED",
"message": "API key is required."
}
}
| Error Code | Cause |
|---|---|
UNAUTHORIZED | No key provided |
KEY_INVALID | Key not found in the system |
KEY_INACTIVE | Key has been revoked |
KEY_EXPIRED | Key expiry date has passed |
IP_NOT_ALLOWED | Your IP is not on the allowlist |
403 — Feed access denied
{
"error": {
"code": "SYMBOL_NOT_PERMITTED",
"message": "Your key does not have access to feed WMMARKETS:PRIME."
}
}
429 — Rate limit exceeded
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded (minute). Retry after 45 seconds."
}
}
The Retry-After header tells you exactly how long to wait:
Retry-After: 45
X-RateLimit-Exceeded: minute
Best Practices
Store keys in environment variables
import os
API_KEY = os.environ["FOREX_API_KEY"]
const API_KEY = process.env.FOREX_API_KEY;
Never commit keys to git
# .gitignore
.env
*.env
config.local.js
secrets.json
Handle 429 with exponential backoff
import time
import requests
def request_with_retry(url, headers, max_retries=3):
for attempt in range(max_retries):
resp = requests.get(url, headers=headers)
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 60))
wait = retry_after + (2 ** attempt) # add jitter
print(f"Rate limited — waiting {wait}s (attempt {attempt + 1})")
time.sleep(wait)
continue
return resp
raise Exception("Max retries exceeded")
Monitor remaining quota proactively
resp = requests.get(url, headers=headers)
remaining = int(resp.headers.get("X-RateLimit-Remaining-Minute", 999))
if remaining < 10:
print(f"Warning: only {remaining} requests left this minute")