Skarb API
Your data in Skarb by a token: send trades by script on every plan, read your portfolio and scores and connect an AI agent by MCP from Pro — on Ultra the agent acts as well. For yourself only and non-commercially — the terms.
Who can use it
Sending your own data — on every plan. Reading your portfolio and scores and MCP for AI agents — from Pro: on Pro the agent reads, on Ultra it acts as well.
| Plan | Sending data | Reading | MCP |
|---|---|---|---|
| Base | yes | — | — |
| Pro | yes | yes | reading |
| Ultra | yes | yes | reading and actions |
Quick start
Make a token in the settings, the “API” tab, and confirm it with the code the bot sends you. It is shown once — save it right away.
Check the token works:
curl https://api.skarb.dev/v1/me \
-H "Authorization: Bearer skb_…"Make a source of your own — a connection with its account in the portfolio you pick:
curl -X POST https://api.skarb.dev/v1/connections \
-H "Authorization: Bearer skb_…" \
-H "Content-Type: application/json" \
-d '{"name": "My broker", "portfolio": "main", "currency": "USD"}'Send trades — up to 1000 rows at a time. A share by its ISIN, a fund by its ticker and the venue’s MIC, a coin by its symbol:
curl -X POST https://api.skarb.dev/v1/connections/<id>/trades \
-H "Authorization: Bearer skb_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: batch-2026-10-08" \
-d '{
"trades": [
{
"externalId": "t-1001",
"instrument": { "assetClass": "stock", "isin": "US0378331005" },
"side": "buy",
"quantity": "10",
"price": "187.50",
"currency": "USD",
"fee": "1.00",
"executedAt": "2026-09-30T14:32:10+02:00"
},
{
"externalId": "t-1002",
"instrument": { "assetClass": "etf", "ticker": "VWCE", "mic": "XETR" },
"side": "buy",
"quantity": "5",
"price": "121.30",
"currency": "EUR",
"executedAt": "2026-10-01T10:05:00+02:00"
},
{
"externalId": "t-1003",
"instrument": { "assetClass": "crypto", "symbol": "BTC" },
"side": "buy",
"quantity": "0.05",
"price": "62000",
"currency": "USDT",
"executedAt": "2026-10-02T08:00:00Z"
}
]
}'Open your portfolio: the holdings are there, the cost by FIFO.
The same in Python — reading the portfolio (from Pro):
import requests
API = "https://api.skarb.dev/v1"
HEADERS = {"Authorization": "Bearer skb_…"}
portfolio = requests.get(f"{API}/portfolio", headers=HEADERS, timeout=30)
portfolio.raise_for_status()
for position in portfolio.json()["positions"]:
print(position["instrument"]["ticker"], position["marketValue"], position["weight"])Authorization and safety
The token goes in the Authorization: Bearer skb_… header only. In the address, a cookie or another header it is refused.
A token is shown once; only its hash is kept. Revoking it in the settings works at once.
A token has an expiry, scopes (sending, reading) and, on Ultra, a list of addresses. It can be tied to one connection or one portfolio — then it sees no other.
A token leaked — revoke it and make a new one. Twenty wrong tokens from one address in ten minutes shut the address out for an hour.
Limits
| Limit | Base | Pro | Ultra |
|---|---|---|---|
| Requests a minute | 30 | 60 | 300 |
| Requests a day | 500 | 5000 | 20000 |
| Rows of data a day | 2000 | 10000 | 50000 |
| Instruments a connection | 30 | 100 | 300 |
| Tokens | 1 | 3 | 10 |
The day’s limits start over at midnight in Kyiv.
Every answer carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, and the day’s rest X-Quota-Daily-Limit and X-Quota-Daily-Remaining. On a 429, wait the seconds Retry-After says.
A batch sent again within a day with the same Idempotency-Key gets the same answer and writes nothing twice. Put one on every batch.
Sending data
A connection is your own source of data with its account in the portfolio you pick. Trades and movements of money come in batches of up to 1000 rows.
A row with the same externalId is the same row: sent again it makes no double, changed it is updated.
An instrument: a share or a fund by its ISIN or its ticker with the venue’s MIC, a coin by its symbol. Skarb looks for it at once; not found, the row waits as pending, and you can match it by hand on the accounts page.
Coin trades in USDT and USDC are kept in dollars one to one. Movements of money are signed: a deposit above zero, a withdrawal below.
Emptying a connection (reset) and deleting it (DELETE) need the X-Confirm header — neither can be undone.
Reading
From Pro a token with the read scope gives your own data and Skarb’s derivatives of it. The portfolio is the main and the secondary together, as on the site, unless slot names one.
| Request | What it does |
|---|---|
| GET /v1/me | Who you are: the plan, the token, its limits and today’s use |
| GET /v1/connections | Your own sources and what their rows hold |
| POST /v1/connections | Make a source of your own, with its account |
| GET /v1/connections/{id} | One source |
| POST /v1/connections/{id}/reset | Take out everything a source sent, and what was made of it |
| DELETE /v1/connections/{id} | Delete a source with its account; the tokens tied to it are revoked |
| POST /v1/connections/{id}/trades | Send trades |
| POST /v1/connections/{id}/cash-flows | Send deposits, withdrawals, dividends, interest, fees and taxes |
| GET /v1/connections/{id}/rows | The rows sent, with their status |
| DELETE /v1/connections/{id}/rows/{externalId} | Take out one row and what was made of it |
| GET /v1/portfolios | Your portfolios and the accounts in each |
| GET /v1/portfolio | The holdings with value, weight and score, and the totals |
| GET /v1/portfolio/allocation | The portfolio by class, sector and currency |
| GET /v1/portfolio/health | The portfolio’s health and its checks |
| GET /v1/portfolio/trades | Every trade of the portfolio, newest first; the exchanges’ spot in dollars |
| GET /v1/portfolio/cash-flows | Every movement of money, newest first |
| GET /v1/scores | Skarb’s scores by ticker |
| GET /v1/alerts | Your alerts |
| GET /v1/diary | Your diary, newest first |
| GET /v1/macro | The cycle’s phase and what the central banks are doing |
Scores — up to 20 tickers at a time. The score of what you hold or watch is free; the rest are taken off the same daily count of asset scores as on the site and in the bot.
The answer of GET /v1/portfolio:
{
"asOf": "2026-10-08T09:30:00.000Z",
"slot": "all",
"baseCurrency": "USD",
"totals": {
"marketValue": "2000",
"cost": "1500",
"unrealizedPnl": "500",
"unrealizedPnlPct": "33.33",
"realizedPnl": "0",
"cash": []
},
"positions": [
{
"instrument": {
"id": "…",
"ticker": "AAPL",
"name": "Apple Inc.",
"assetClass": "stock",
"holdingClass": "stock",
"currency": "USD",
"sector": "Information Technology"
},
"quantity": "10",
"avgCost": "150",
"lastPrice": "200",
"marketValue": "2000",
"weight": "100",
"unrealizedPnl": "500",
"unrealizedPnlPct": "33.33",
"score": { "quality": "72.4", "signal": 1, "confidence": "high", "asOf": "2026-10-07" }
}
]
}What the API does not and will not give: quotes, candles, price history, on-chain series, derivatives, insider trades, funds’ holdings, filings and calendars. The rights to those belong to their sources. A price is there only as the value of a holding you have.
MCP
The MCP server gives an AI agent — Claude Code, Cursor and others — your data in Skarb, and on Ultra its actions too. You need a token with the MCP scope — Pro has it. A call of a tool is one request of the API out of the day’s limit; the handshake and the list of tools are free. What to ask the agent and what it cannot do — on the page Skarb in an AI agent.
Claude Code, in one command:
claude mcp add --transport http skarb https://api.skarb.dev/mcp --header "Authorization: Bearer skb_…"Cursor and other clients — JSON with the address and the header:
{
"mcpServers": {
"skarb": {
"url": "https://api.skarb.dev/mcp",
"headers": { "Authorization": "Bearer skb_…" }
}
}
}claude.ai and ChatGPT connectors connect by signing in to Skarb, with no token: the server is https://api.skarb.dev/mcp, then the browser opens the sign-in and “Allow”. Claude Code the same way, with no header:
claude mcp add --transport http --scope user skarb https://api.skarb.dev/mcpFor authors of clients
Skarb is its own authorization server: OAuth 2.1, the code flow with PKCE S256. Metadata — /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server on api.skarb.dev. A client registers itself (RFC 7591, POST /oauth/register), with no secret. A redirect URI is taken only over https to a known host — Claude, ChatGPT, Cursor and their subdomains — or to this computer: http://localhost, 127.0.0.1 and [::1] on any port. Need another host — write to us. An access token lives an hour, a refresh token 30 days with rotation: each exchange gives a new one, and the old one is taken again for 30 seconds only.
ChatGPT Pro gives an agent the reading tools alone: evaluate_trade and the other actions work on ChatGPT Business, Enterprise and Edu.
| Tool | What it does |
|---|---|
| get_me | The plan, the token, its limits and today’s use |
| get_portfolio | The holdings with value, weight and score, and the totals |
| get_allocation | The portfolio by class, sector and currency |
| get_health | The portfolio’s health and its checks |
| get_trades | Every trade of the portfolio, the exchanges’ spot among them, newest first |
| get_score | Skarb’s score of a ticker; free for what you hold or watch |
| list_alerts | Your alerts |
| list_diary | Your diary, newest first |
| get_macro | The cycle’s phase and what the central banks are doing |
| list_connections | Your connections |
| get_watchlists | Your watchlists: ids, names and the tickers on each, no prices |
| get_correlations | How the holdings move together, and their clusters |
| get_rebalance_plan | The rebalance plan for your targets — Ultra, as the rebalance screen is |
| run_stress_test | The portfolio through a stress scenario, built-in or your own |
| get_latest_release | The text of the company’s latest earnings release at the SEC — the press release, exhibit 99.1, or by exhibit its presentation, prepared remarks or transcript where it filed one — with its date and address; 40,000 characters at most |
| get_asset_news | The company’s own SEC filings of one of your assets — held or watched: 8-K with its items, 10-Q and 10-K, each with its date and address, no text; nothing while the news is off in the settings |
| get_futures | Your closed futures positions on your exchanges — side, leverage, entry and exit, opened and closed, the result, fees and funding — newest first, and the period in sum: the win rate, the time held, the result by coin, side, leverage and ticker |
| Tool | What it does |
|---|---|
| evaluate_trade | A pre-trade check: flags, two or three questions and its id; one of the day’s checks |
| answer_trade_check | The answers to a check’s questions — the light, and why |
| rebalance_what_if | The rebalance plan for other targets, method or new money, unsaved; one of the day’s what-ifs |
| create_alert | A new price alert, within the plan’s number of alerts |
| delete_alert | Delete an alert |
| update_alert | Change an alert — the crossing, the level, the repeat, the conditions; saving starts it again |
| pause_alert | Stop an alert — not watched, and no place taken among the plan’s alerts |
| resume_alert | Start an alert again — stopped, fired or run out, with a fresh month |
| create_diary_entry | A diary entry on an asset, with reminders; one of the day’s entries |
| add_to_watchlist | Add a ticker to a watchlist — the main one, or one named by its id |
| remove_from_watchlist | Take a ticker off a watchlist — the main one, or one named by its id |
Actions spend the same daily limits as the site and the bot: trade checks, rebalance what-ifs and diary entries, while alerts and the watchlist stop at the plan’s size. There are no tools with quotes, candles, on-chain data or derivatives — for the same reason as in the API.
Errors
An error is JSON with one field, error: a code for a program, a message for a person, and on a 422 the fields that did not pass.
{
"error": {
"code": "validation_failed",
"message": "A decimal string, such as \"12.5\"",
"details": [{ "path": "trades[0].price", "issue": "A decimal string, such as \"12.5\"" }]
}
}| HTTP | Codes |
|---|---|
| 400 | bad_request |
| 401 | unauthorized, token_expired, token_revoked |
| 403 | forbidden_scope, forbidden_plan, ip_not_allowed, terms_not_accepted, limit_connections, limit_history |
| 404 | not_found, connection_not_found, feature_disabled |
| 405 | method_not_allowed |
| 409 | idempotency_in_progress |
| 413 | payload_too_large |
| 422 | validation_failed, batch_too_large, idempotency_conflict, confirm_required |
| 429 | rate_limited, quota_exceeded |
| 500 | internal |
OpenAPI
The whole contract is openapi.json (OpenAPI 3.1). It is built from the same schemas the API checks requests with, so it does not drift from the code.