Quickstart
Store your key, call /v1/me to verify auth, then start reading signals or trades. Five minutes from zero to first API response.
curl https://app.pipsync.io/api/v1/me \ -H "Authorization: Bearer $PIPSYNC_KEY"
Every response is wrapped in { "data": … }. Paginated endpoints additionally return a "meta" object with total, page, totalPages, and hasMore.
Authentication
All API requests require a bearer token in the Authorization header. Keys are per-environment (live / sandbox), per-scope, and time-limited only if you choose to set an expiry.
Obtaining an API key
API access requires the Enterprise plan. Once on Enterprise:
- Navigate to Settings → API Keys in your workspace.
- Click Create key, choose a name (e.g. prod-signal-reader), and select the scopes you need.
- Copy the key — it is shown only once. Store it in a secret manager (AWS Secrets Manager, Vault, Doppler, etc.).
- To rotate: create a replacement key, cut traffic over, revoke the old key. There is no downtime if you overlap by one deployment cycle.
Key scopes
| Scope | Access | Endpoints |
|---|---|---|
signals:read | Read-only | GET /v1/signals |
trades:read | Read-only | GET /v1/trades |
reports:read | Read-only | GET /v1/reports, GET /v1/reports/trades |
account:read | Read-only | GET /v1/me, GET /v1/account/usage |
webhooks:write | Write | Manage webhook subscriptions (dashboard only) |
Key security
Additional hardening available on Enterprise: IP allowlist (restrict which CIDR ranges may use a key) and IP blocklist. Both configured per-key from the same settings page.
Rate limits
Limits are per API key per rolling 60-second window. Webhooks are separately rate-limited on their inbound delivery surface.
| Plan | API req / min | Webhooks / min | Burst | Notes |
|---|---|---|---|---|
| Basic | 60 | 10 | 1× | No burst headroom |
| Pro | 300 | 60 | 2× | Short burst window (5 s) |
| Business | 1000 | 200 | 3× | Burst up to 3 000 RPM for ≤ 5 s |
| Enterprise | Custom | Custom | Custom | Negotiated SLA, custom concurrency |
Response headers
Every API response includes rate-limit headers so your integration can stay within bounds without trial and error:
| Header | Value |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the current window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | ISO 8601 timestamp when the window resets |
Retry-After | Seconds to wait before retrying (only on 429) |
Retry-After and honour it. Do not hard-code a sleep duration — the exact window depends on your plan and current load.Exponential backoff
Implement jittered exponential backoff whenever you receive a 429. This pattern prevents thundering-herd issues if your fleet is calling in parallel:
async function fetchWithRetry(url, key, maxRetries = 4) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const res = await fetch(url, {
headers: { Authorization: `Bearer ${key}` },
});
if (res.status !== 429) return res.json();
const retryAfter = parseInt(res.headers.get("Retry-After") ?? "1", 10);
const backoff = retryAfter * 1000 * Math.pow(2, attempt) + Math.random() * 200;
await new Promise(r => setTimeout(r, backoff));
}
throw new Error("Rate limit retries exhausted");
}Error codes
All errors use RFC 7807 Problem Details (application/problem+json):
{
"type": "https://app.pipsync.io/errors/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "Missing or invalid API key"
}| HTTP status | Meaning | Action |
|---|---|---|
400 | Bad Request | Fix the request body / query params |
401 | Unauthorized | Check key value and Authorization header format |
403 | Forbidden | Key lacks the required scope for this endpoint |
404 | Not Found | Resource does not exist or was deleted |
422 | Unprocessable | Passes schema but business-rule validation failed |
429 | Rate Limited | Back off and retry (see Retry-After) |
5xx | Server Error | Transient — retry with backoff; check pipsync.io/status |
GET /v1/me
Returns the profile of the workspace owner associated with the API key.
curl https://app.pipsync.io/api/v1/me \ -H "Authorization: Bearer $PIPSYNC_KEY"
GET /v1/signals
Returns parsed trading signals for the workspace. Signals are the normalized output of the parser — ready for downstream processing.
Query parameters
| Param | Type | Required | Description |
|---|---|---|---|
since | ISO 8601 | No | Return signals received at or after this time |
limit | number | No | Max results per page (default 25, max 100) |
page | number | No | Page number (1-based) |
instrument | string | No | Filter by instrument, e.g. EURUSD |
status | string | No | Filter by status: parsed | pending | invalid |
curl "https://app.pipsync.io/api/v1/signals?limit=10" \ -H "Authorization: Bearer $PIPSYNC_KEY"
GET /v1/trades
Returns trade records (opened, closed, partially-closed positions) for the workspace. Paginated; newest trades first.
Query parameters
| Param | Type | Required | Description |
|---|---|---|---|
since | ISO 8601 | No | Only trades opened at or after this time |
limit | number | No | Max results per page (default 25, max 100) |
page | number | No | Page number (1-based) |
instrument | string | No | Filter by instrument |
status | string | No | Filter by status: open | closed | pending |
curl "https://app.pipsync.io/api/v1/trades?limit=25" \ -H "Authorization: Bearer $PIPSYNC_KEY"
GET /v1/reports
Enumerate generated trade reports and download links. Use /v1/reports/trades to get the detailed line-item CSV/PDF export.
Realtime stream
For browser dashboards and live monitoring, subscribe to /api/trading/stream with EventSource. This is the shipped SSE fallback when WebSocket upgrades are unavailable; keep signed webhooks for server-to-server automation.
Webhooks
Subscribe to push events from Settings → Webhooks. PipSync POSTs a signed JSON payload to your endpoint every time an event occurs. Delivery is attempted up to 5 times with exponential backoff.
| Event | When | Payload type |
|---|---|---|
signal.received | A signal was parsed from any source | SignalIntent |
signal.parsed | Parser normalized the source message | ParsedSignal |
trade.opened | A broker position was opened | TradeOpened |
trade.closed | A broker position was closed | TradeClosed |
trade.tp_hit | Take profit was hit | TradeClosed |
trade.sl_hit | Stop loss was hit | TradeClosed |
subscription.upgraded | Subscription moved to a higher tier | Subscription |
subscription.canceled | Cancellation was scheduled | Subscription |
Verifying signatures
Every webhook delivery includes an X-PipSync-Signature header formatted as t=<unix-ts>,v1=<hex-hmac-sha256>. Verify before trusting the payload:
import crypto from "crypto";
export function verifyWebhook(rawBody: string, header: string, secret: string): boolean {
const parts = Object.fromEntries(
header.split(",").map((part) => part.split("=")),
);
const signed = parts.t + "." + rawBody;
const expected = crypto
.createHmac("sha256", secret)
.update(signed)
.digest("hex");
// Constant-time comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(parts.v1 ?? ""),
);
}t is more than 5 minutes in the past — this prevents replay attacks. Compare Math.abs(Date.now() / 1000 - Number(parts.t)) > 300.OpenAPI clients
The shipped contract is the OpenAPI 3.1 spec at /api/v1/openapi.json. Generate a typed client in your runtime — or use the interactive playground for ad-hoc requests.