Candella
CopytradeQuantLead loginDashboard
Sign inSign up
Copytrade / Developers
Candella SDK

Deploy your trading algo without building broker infrastructure

Run your strategy on your own data and infrastructure. Send Candella one signed signal per trade and it becomes a real order across your followers' connected brokerage accounts, with manual approval and the risk caps they set. Equities and options today, with crypto on the way. It is the fast alternative to wiring your strategy into a brokerage API like Interactive Brokers.

cc.start_session()                          # arms live execution
webull.place_order("SPY", "buy", 10)        # your execution, your account
cc.signal("SPY", "buy", 10, price=512.30)   # the copy signal, one line
Get your API keysRead the reference
  • →One signed HTTP POST per trade
  • →Callable from any language
  • →Equities and options
  • →Multi-leg options as a single order
  • →Idempotent by construction
  • →Non-custodial, through SnapTrade
Language-agnostic by design

The signal API is a plain HMAC-signed HTTP request, so you can integrate from any language right now. The Python SDK is available today. Native TypeScript, Go, Java, C++, and Rust SDKs are on the way.

API reference

QuickstartAuthenticationSessionsEndpointSigningIdempotency

Quickstart

Send your algo's decisions to Candella with one signed request per trade. Install the Python SDK, or call the signed HTTP API below from any language.

bash
pip install candella
python
from candella import Copytrade

cc = Copytrade(api_key="...", secret="...", strategy_id="my-algo")

cc.start_session()                          # arms live execution at market open
cc.signal("SPY", "buy", 10, price=512.30)   # copies to your followers
cc.signal_option("SPY", "BTO", 5, price=3.10,
                 expiry="2026-07-18", strike=520, right="C")
cc.end_session()                            # when your algo sleeps

Authentication

Generate an API key and secret on your Algo keys page. Three values identify and sign every request.

ValuePurpose
api_keyPublic key. Sent as the X-Candella-Key header. Identifies your account.
secretSigns every request with HMAC-SHA256. Shown once at creation; store it safely, it cannot be retrieved again.
strategy_idNames this strategy. Sent as strategyId in the body.

Sessions are the consent gate

Signals only reach real accounts while you have an active session. Open one with start_session() when your algo wakes and close it with end_session() when it sleeps. A signal sent with no active session is authenticated, validated, and recorded, then dropped before any order is placed. That is how you integration-test safely: send signals without opening a session and nothing hits a real account.

Endpoint

A single endpoint accepts every signal.

http
POST https://candella.dev/api/ingest/algo-signal

Request body

FieldTypeRequiredNotes
strategyIdstringyesYour strategy id.
signalIdstringyesStable, deterministic id (your order id or a content hash), never random. Powers idempotency.
symbolstringyesTicker for a stock, or the underlying for an option.
assetType"stock" | "option"yesCrypto is on the way.
side"BUY" | "SELL" | "BTO" | "STC"yesOpen or close, equity or option.
quantitynumber > 0yesShares for a stock, contracts for an option.
pricenumber > 0yesUsed for position sizing and the slippage guard.
emittedAtstringyesISO-8601 timestamp of when your algo fired.
optionLegobjectfor optionsRequired when assetType is "option": { underlying, expiry "YYYY-MM-DD", strike, right "C" | "P" }.
bookSizeUSDnumber > 0noVirtual book size for a signal-only strategy with no funded account to size against.

Headers

HeaderValue
Content-Typeapplication/json
X-Candella-KeyYour api_key.
X-Candella-TimestampCurrent epoch milliseconds, as a string.
X-Candella-SignatureLowercase hex HMAC-SHA256. See Signing.

Responses

StatusMeaning
202 acceptedAccepted. Fans out to your followers if a session is active; with no session it is recorded and dropped at the consent gate, no order placed.
202 duplicateIdempotent re-send (same strategyId and signalId already seen). No second order.
400 invalid_signalThe body failed validation. The detail field lists the offending fields.
400 invalid_jsonThe body was not valid JSON.
401 unauthorizedSignature or timestamp check failed. detail is one of: missing_fields, bad_timestamp, stale, bad_signature.
401 missing_key / unknown_keyNo API key, or a key that did not resolve to an account.
422 guard_blockedBlocked by a notional or contract-size limit. detail carries the reason.
429 rate_limitedToo many signals in a short window. Retry after the seconds in detail.

Signing a request

Every request is signed. The Python SDK does this for you; here is the exact recipe for any other language.

  1. Canonicalize the body: compact JSON with keys sorted lexicographically and no whitespace. These exact bytes are what you both sign and send.
  2. Timestamp: the current epoch milliseconds, as a string.
  3. Sign: HMAC-SHA256 over the string `${timestamp}.${rawBody}`, using your secret as raw UTF-8 bytes, encoded as lowercase hex. That is your signature.
  4. Send the three headers: X-Candella-Key, X-Candella-Timestamp, and X-Candella-Signature.
  5. POST the exact signed bytes. Do not re-serialize, reorder keys, or pretty-print after signing, or the signature will not match.

Requests more than five minutes from server time in either direction are rejected as stale, so sign and send promptly rather than pre-computing signatures.

bash
SECRET='your-secret'
KEY='your-api-key'

# Compact JSON, keys sorted. The identical bytes are signed and sent.
BODY='{"assetType":"stock","emittedAt":"2026-06-26T14:30:00+00:00","price":512.30,"quantity":10,"side":"BUY","signalId":"order-12345","strategyId":"my-algo","symbol":"SPY"}'

TS=$(python3 -c 'import time; print(int(time.time()*1000))')

SIG=$(printf '%s.%s' "$TS" "$BODY" \
  | openssl dgst -sha256 -hmac "$SECRET" -hex \
  | sed 's/^.*= //')

curl -sS https://candella.dev/api/ingest/algo-signal \
  -H "Content-Type: application/json" \
  -H "X-Candella-Key: $KEY" \
  -H "X-Candella-Timestamp: $TS" \
  -H "X-Candella-Signature: $SIG" \
  --data "$BODY"

Idempotency

Re-sending a signal must never double-fire a real order. Pass a stable, deterministic signalId (your algo's own order id, or a content hash of the signal), never a random one. Candella keys the order off your strategyId and signalId, so an identical re-send collapses to a no-op instead of a second order. The Python SDK derives one for you if you omit it; a raw HTTP integrator must supply it.

Candella Labs

Execution infrastructure for self-directed traders. Copytrade mirrors proven traders into your own brokerage; Quant is research and backtesting.

Products
QuantCopytradeCandella SDK Docs
Browse LeadsApply to LeadContact
Legal
Terms of ServiceCopytrade AddendumRisk DisclosureAuto-ExecutionPrivacy Policy
GLBA NoticeCookiesAcceptable UseDMCA
© 2026 Lutz Consulting Group, LLC, d/b/a Candella Labs. All rights reserved.
Candella™ and Candella Copytrade™ are trademarks of Lutz Consulting Group, LLC.