API reference
Bearer-authenticated REST for Pro accounts. Versioned under /v1/.
Authentication
Issue a key from Settings under the API tab. The raw key is shown exactly once; copy it before closing the dialog. You can have up to 5 active keys per account, and revoking a key takes effect immediately.
Authorization: Bearer ss_live_<your-key>
Free-tier accounts can issue keys but cannot call the API. Upgrade to Pro.
Rate limits
100 requests per minute per key, sliding window. Exceeding the limit returns 429 RATE_LIMITED with Retry-After, X-RateLimit-Limit, and X-RateLimit-Remaining headers.
Response envelopes
Successful responses return:
{ "data": <payload>, "meta": { ... } }Errors return:
{ "error": { "code": "STRING", "message": "human-readable" } }Common error codes:
- UNAUTHORIZED (401): missing or malformed bearer
- INVALID_KEY (401): key not found or revoked
- FORBIDDEN (403): non-Pro tier
- NOT_FOUND (404): resource does not exist or is not yours
- VALIDATION_ERROR (400): bad request body
- RATE_LIMITED (429): over the per-key limit
- STABLECOIN (400): analysis attempted on a pegged asset
- DB_ERROR (500): backend persistence failure
Endpoints
Eleven routes across analysis, positions, alerts, portfolio, and market data. Every route requires a Pro key.
| Method | Path | Tier | Description |
|---|---|---|---|
| POST | /api/v1/analysis | Pro | Run a new analysis. Supported types: exit_plan, health_check, market_brief. Returns 201 with the persisted analysis, also retrievable via the GET route. |
| GET | /api/v1/analysis/:id | Pro | Fetch a previously stored analysis. Scoped to your own analyses. |
| GET | /api/v1/positions | Pro | List your positions ordered newest first. |
| POST | /api/v1/positions | Pro | Create a position. |
| PATCH | /api/v1/positions/:id | Pro | Update entry_price, quantity, entry_date, or notes. Send only the fields you want to change. |
| DELETE | /api/v1/positions/:id | Pro | Hard delete. Returns 204 on success. |
| GET | /api/v1/alerts | Pro | List your price alerts, active and historical. |
| POST | /api/v1/alerts | Pro | Create an active price alert. |
| DELETE | /api/v1/alerts/:id | Pro | Soft cancel. The alert row is kept (status "cancelled") so triggered history stays intact. |
| GET | /api/v1/portfolio | Pro | Aggregate snapshot. Returns per-holding cost basis, market value, and P&L, plus a portfolio-wide summary. The priceCoverage field reports how many of your symbols had a live price (0 to 1); when below 1, totals are null rather than misleadingly partial. |
| GET | /api/v1/market/snapshot/:symbol | Pro | The same data the dashboard uses: live price, 7, 30, 90, and 365 day price and volume history, 30-day OHLCV candles, on-chain top-holder concentration, global market metrics, the fear and greed index, DefiLlama protocol fundamentals, and CryptoCompare social sentiment. Cached server-side; safe to poll. |
Example requests
Run an analysis
curl -X POST https://www.sellsignal.app/api/v1/analysis \
-H "Authorization: Bearer $SS_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "exit_plan",
"symbol": "BTC",
"position": { "entryPrice": 62000, "quantity": 1.0 },
"riskProfile": "moderate"
}'Create a position
curl -X POST https://www.sellsignal.app/api/v1/positions \
-H "Authorization: Bearer $SS_KEY" \
-H "Content-Type: application/json" \
-d '{ "symbol": "ETH", "entry_price": 3200, "quantity": 2.5 }'Create an alert
curl -X POST https://www.sellsignal.app/api/v1/alerts \
-H "Authorization: Bearer $SS_KEY" \
-H "Content-Type: application/json" \
-d '{ "symbol": "SOL", "target_price": 250, "direction": "above" }'Quickstart: JavaScript
const SS_KEY = process.env.SS_KEY;
const res = await fetch("https://www.sellsignal.app/api/v1/portfolio", {
headers: { Authorization: `Bearer ${SS_KEY}` },
});
if (!res.ok) {
const { error } = await res.json();
throw new Error(`${error.code}: ${error.message}`);
}
const { data } = await res.json();
console.log(data.summary);Quickstart: Python
import os, requests
SS_KEY = os.environ["SS_KEY"]
r = requests.get(
"https://www.sellsignal.app/api/v1/portfolio",
headers={"Authorization": f"Bearer {SS_KEY}"},
)
r.raise_for_status()
print(r.json()["data"]["summary"])Not in v1
- Webhooks (will be its own version)
- OpenAPI spec / SDK generation
- Per-key scopes (read-only vs read-write)
- Analysis types beyond exit_plan, health_check, and market_brief. Ask if you need comparison or scenario over the API.