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).