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

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:

RestrictionDescription
Feed permissionsKey only accesses specific feeds (e.g., WMMARKETS:ZERO only)
RPMMaximum requests per minute
RPDMaximum requests per day
IP allowlistIf set, only listed IP addresses are accepted
Expiry dateKey is automatically deactivated after this date
WS connectionsMaximum simultaneous WebSocket connections
WS symbolsMaximum 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
HeaderMeaning
X-RateLimit-Limit-MinutePer-minute request cap
X-RateLimit-Remaining-MinuteRequests remaining this minute
X-RateLimit-Reset-MinuteUnix timestamp when the minute window resets
X-RateLimit-Limit-DayDaily request cap
X-RateLimit-Remaining-DayRequests remaining today

Authentication Errors

401 — Missing or invalid key

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "API key is required."
  }
}
Error CodeCause
UNAUTHORIZEDNo key provided
KEY_INVALIDKey not found in the system
KEY_INACTIVEKey has been revoked
KEY_EXPIREDKey expiry date has passed
IP_NOT_ALLOWEDYour 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")