This is the full developer documentation for Traderz Signals API
# Traderz Signals API
> Send trading signals to Traderz Labs over a single authenticated HTTP endpoint.
The Traderz Signals API ingests trading signals from **desks** over HTTP. A desk authenticates with an API key and `POST`s signals to a single endpoint; each signal is attributed to the desk and stored.
## How it works
[Section titled “How it works”](#how-it-works)
1. **Register your desk** and create an API key in the [Desk Console](https://app.traderz.dev).
2. **Send signals** to `POST /v1/signals` with your key in the `Authorization` header.
3. Repeats carrying the same `client_signal_id` are **idempotent** (deduplicated).
```bash
curl -X POST https://api.traderz.dev/v1/signals \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{"source":"binance","type":"crypto","symbol":"ETH/USDT","side":"open","direction":"long"}'
```
## Next steps
[Section titled “Next steps”](#next-steps)
* [Quickstart](/quickstart/) — send your first signal in a few minutes.
* [Authentication](/authentication/) — get and manage API keys.
* [POST /v1/signals](/reference/signals/) — the full request/response reference.
* [Code examples](/examples/) — curl, JavaScript, and Python.
## For LLMs
[Section titled “For LLMs”](#for-llms)
This site publishes machine-readable indexes for AI tools:
* [`/llms.txt`](/llms.txt) — a concise index of the documentation.
* [`/llms-full.txt`](/llms-full.txt) — the full documentation as a single Markdown file.
The raw OpenAPI 3.1 spec is also available from the API at `/openapi.yaml`.
# Authentication
> How desks authenticate to the Traderz Signals API with API keys.
Every request to `POST /v1/signals` must carry a valid **API key**. Keys are issued and managed by your desk in the [Desk Console](https://app.traderz.dev).
## Sending the key
[Section titled “Sending the key”](#sending-the-key)
Use either header (Bearer is preferred):
```http
Authorization: Bearer tzk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
```http
X-API-Key: tzk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
A missing or invalid key returns **`401 Unauthorized`**:
```json
{ "error": "unauthorized", "message": "invalid api key" }
```
## Managing keys
[Section titled “Managing keys”](#managing-keys)
In the console you can:
* **Create** a key — the plaintext is shown **once** at creation and never again. Store it in your secret manager.
* Hold **multiple** keys per desk — issue one per bot or environment so you can rotate without downtime.
* **Revoke** a key — it stops authenticating immediately. Signals already stored are kept.
## Security notes
[Section titled “Security notes”](#security-notes)
* Keys are stored only as a SHA-256 hash on our side; we cannot recover a lost key — create a new one and revoke the old.
* Treat keys like passwords: never commit them to source control or expose them in a browser. Signals are sent **server-side**.
* All requests must use **HTTPS**.
# Code examples
> Send signals from curl, JavaScript, and Python.
All examples read the API key from an environment variable. Never hard-code keys.
## curl
[Section titled “curl”](#curl)
```bash
export TRADERZ_API_KEY="tzk_..."
curl -X POST https://api.traderz.dev/v1/signals \
-H "Authorization: Bearer $TRADERZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": "binance",
"type": "crypto",
"symbol": "ETH/USDT",
"side": "open",
"direction": "long",
"price": 3450.5,
"client_signal_id": "trade-8842-open"
}'
```
## JavaScript (Node / fetch)
[Section titled “JavaScript (Node / fetch)”](#javascript-node--fetch)
```js
const res = await fetch("https://api.traderz.dev/v1/signals", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.TRADERZ_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
source: "binance",
type: "crypto",
symbol: "ETH/USDT",
side: "open",
direction: "long",
price: 3450.5,
client_signal_id: "trade-8842-open",
}),
});
if (!res.ok) throw new Error(`signal rejected: ${res.status}`);
const { id, deduped } = await res.json();
console.log(deduped ? `already sent as ${id}` : `stored ${id}`);
```
## Python (requests)
[Section titled “Python (requests)”](#python-requests)
```python
import os, requests
resp = requests.post(
"https://api.traderz.dev/v1/signals",
headers={"Authorization": f"Bearer {os.environ['TRADERZ_API_KEY']}"},
json={
"source": "binance",
"type": "crypto",
"symbol": "ETH/USDT",
"side": "open",
"direction": "long",
"price": 3450.5,
"client_signal_id": "trade-8842-open",
},
timeout=10,
)
resp.raise_for_status()
data = resp.json()
print("deduped" if data.get("deduped") else "stored", data["id"])
```
# Quickstart
> Register a desk, create an API key, and send your first signal.
## 1. Create an API key
[Section titled “1. Create an API key”](#1-create-an-api-key)
Sign in to the [Desk Console](https://app.traderz.dev), open **API keys**, and click **Create key**. The full key is shown **once** — copy it somewhere safe. It looks like:
```plaintext
tzk_b083b68c06cf1330c0fd9eaa93b33c1055d31b66424fc5a25b2897b90a7842f5
```
## 2. Send a signal
[Section titled “2. Send a signal”](#2-send-a-signal)
```bash
curl -X POST https://api.traderz.dev/v1/signals \
-H "Authorization: Bearer $TRADERZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": "binance",
"type": "crypto",
"symbol": "ETH/USDT",
"side": "open",
"direction": "long",
"price": 3450.5
}'
```
A successful call returns **`201 Created`**:
```json
{ "id": "019ebac868e4b0d9db93cc0daccca63d", "received_at": 1781250156772, "deduped": false }
```
## 3. Make it idempotent (recommended)
[Section titled “3. Make it idempotent (recommended)”](#3-make-it-idempotent-recommended)
Include a `client_signal_id` so safe retries never create duplicates:
```bash
-d '{ "source":"binance", "type":"crypto", "symbol":"ETH/USDT", "side":"open",
"client_signal_id":"trade-8842-open" }'
```
Re-sending the same `client_signal_id` returns **`200 OK`** with `"deduped": true` and the original signal’s `id`. See [Idempotency](/reference/idempotency/).
## 4. Rotate or revoke
[Section titled “4. Rotate or revoke”](#4-rotate-or-revoke)
Create additional keys (e.g. one per bot) and **revoke** any key from the console at any time — revoked keys stop authenticating immediately.
# Errors
> Error model and status codes returned by the Traderz Signals API.
Errors are returned as JSON with an `error` code and, where useful, a `message` or a `fields` map. Always branch on the HTTP **status code** first.
| Status | `error` | Meaning | How to fix |
| ------ | ------------------- | ------------------------------- | --------------------------------------------------------------------------------------- |
| `400` | `bad_request` | The body was not valid JSON. | Send well-formed JSON with `Content-Type: application/json`. |
| `401` | `unauthorized` | Missing or invalid API key. | Send a valid key (see [Authentication](/authentication/)); create a new one if revoked. |
| `422` | `validation_failed` | One or more fields are invalid. | Inspect `fields` and correct each entry. |
| `500` | `internal_error` | Unexpected server error. | Retry with backoff; if it persists, contact support. |
## Validation errors (`422`)
[Section titled “Validation errors (422)”](#validation-errors-422)
The `fields` object maps each invalid field to a reason:
```json
{
"error": "validation_failed",
"fields": {
"side": "must be one of: open, close",
"source": "is required"
}
}
```
## Recommended client handling
[Section titled “Recommended client handling”](#recommended-client-handling)
* `2xx` — success. On `200` check `deduped` to know it was a retry.
* `400` / `422` — **do not retry** unchanged; the request is malformed or invalid.
* `401` — fix credentials before retrying.
* `429` / `5xx` — retry with exponential backoff and jitter. Pair retries with a `client_signal_id` so they stay [idempotent](/reference/idempotency/).
# Idempotency
> Use client_signal_id to make signal submission safe to retry.
Network calls fail and retry. To make sure a retried submission never creates a duplicate signal, send a **`client_signal_id`** — a stable string you generate per logical signal.
## How it works
[Section titled “How it works”](#how-it-works)
* The first request with a given `client_signal_id` is stored and returns **`201 Created`** with `"deduped": false`.
* Any later request from the **same desk** with the **same** `client_signal_id` is **not** stored again. It returns **`200 OK`** with `"deduped": true` and the **original** signal’s `id`.
Deduplication is scoped **per desk**: two different desks may use the same `client_signal_id` without colliding.
## Example
[Section titled “Example”](#example)
```bash
# First send → 201 Created, deduped: false
curl -X POST https://api.traderz.dev/v1/signals \
-H "Authorization: Bearer $TRADERZ_API_KEY" -H "Content-Type: application/json" \
-d '{"source":"binance","type":"crypto","symbol":"ETH/USDT","side":"open","client_signal_id":"trade-8842-open"}'
# Same client_signal_id again → 200 OK, deduped: true, same id
```
## Choosing a key
[Section titled “Choosing a key”](#choosing-a-key)
* Make it **deterministic** for a given logical event — e.g. `${strategy}-${trade}-${side}`.
* Keep it **stable across retries** of the same event, and **unique across different** events.
* If you omit `client_signal_id`, every request is stored as a new signal (no dedup).
# POST /v1/signals
> Submit a trading signal. Full request and response reference.
Submit a single trading signal. The signal is attributed to the desk that owns the API key and stored.
```plaintext
POST https://api.traderz.dev/v1/signals
Authorization: Bearer
Content-Type: application/json
```
## Request body
[Section titled “Request body”](#request-body)
| Field | Type | Required | Description |
| ------------------ | ------- | -------- | ------------------------------------------------------------- |
| `source` | string | **yes** | Origin venue: `polymarket`, `binance`, or `ibkr`. |
| `type` | string | **yes** | Instrument class: `prediction_market`, `crypto`, or `stock`. |
| `symbol` | string | **yes** | Instrument symbol, e.g. `ETH/USDT`, `TSLA`, `BTC 15min`. |
| `side` | string | **yes** | `open` or `close`. |
| `direction` | string | no | `long`, `short`, `buy`, or `sell`. |
| `price` | number | no | Execution/limit price. |
| `quantity` | number | no | Size. |
| `signal_time` | integer | no | Your event time, epoch **milliseconds**. |
| `client_signal_id` | string | no | Idempotency key — see [Idempotency](/reference/idempotency/). |
| `metadata` | object | no | Arbitrary JSON (e.g. take-profit / stop-loss). |
```json
{
"source": "binance",
"type": "crypto",
"symbol": "ETH/USDT",
"side": "open",
"direction": "long",
"price": 3450.5,
"quantity": 1.5,
"signal_time": 1716500000000,
"client_signal_id": "abc-123",
"metadata": { "tp": 3600, "sl": 3300 }
}
```
## Responses
[Section titled “Responses”](#responses)
| Status | When | Body |
| -------------------------- | ---------------------------- | ----------------------------------------------------------------- |
| `201 Created` | Stored | `{ "id": "...", "received_at": 1716500000123, "deduped": false }` |
| `200 OK` | Duplicate `client_signal_id` | `{ "id": "...", "deduped": true }` |
| `422 Unprocessable Entity` | Validation failed | `{ "error": "validation_failed", "fields": { ... } }` |
| `400 Bad Request` | Malformed JSON | `{ "error": "bad_request", "message": "..." }` |
| `401 Unauthorized` | Missing/invalid key | `{ "error": "unauthorized", "message": "..." }` |
* `id` — server-generated, time-sortable identifier for the stored signal.
* `received_at` — server receive time, epoch milliseconds (UTC). Present only on `201`.
* `deduped` — `true` when the request matched a previous `client_signal_id`.
See [Errors](/reference/errors/) for the full error model, and the machine-readable [OpenAPI spec](https://api.traderz.dev/openapi.yaml).