eToro Apps
by @marian2js
Enables agents to interact with the eToro API to access market data, portfolio and social features, and execute trades programmatically. Supports both OAuth...
clawhub install etoro-appsπ About This Skill
name: etoro-apps version: 1.0.0 description: Enables agents to interact with the eToro API to access market data, portfolio and social features, and execute trades programmatically. Supports both OAuth SSO and manual API key authentication. homepage: https://api-portal.etoro.com/ metadata: { "openclaw": { "emoji": "π", "category": "finance", "api_base": "https://public-api.etoro.com/api/v1", }, }
eToro Public API
Base URL: https://public-api.etoro.com/api/v1
About
This skill allows to interact with the user's eToro account programatically, including executing trades.
Authentication & Required Headers
The eToro API supports two authentication methods. Both use the same base URL and endpoints β only the auth headers differ.
Method 1 β OAuth SSO (Bearer Token)
If the user authenticated via "Login with eToro" (SSO/OAuth), an access_token is available from the token exchange.
Headers (every request):
x-request-id: unique UUID per requestAuthorization: Bearer Where access_token comes from:
1. User clicks "Login with eToro" β redirects to https://www.etoro.com/sso/ with PKCE challenge.
2. User authenticates on eToro's side.
3. eToro redirects back with an authorization code.
4. The code is exchanged for tokens via POST https://www.etoro.com/sso/oidc/token:
- grant_type=authorization_code
- code=
- redirect_uri=
- code_verifier=
- Plus Authorization: Basic header
5. Response contains:
- access_token β this is the Bearer token for API calls (~2130 chars, JWT)
- id_token β JWT with user identity (sub claim = 128-char encoded user ID)
- token_type: "Bearer"
- expires_in: (varies)
Example:
curl -X GET "https://public-api.etoro.com/api/v1/watchlists" \
-H "x-request-id: " \
-H "Authorization: Bearer "
Method 2 β Manual API Keys
If the user provides API keys manually (no OAuth), use key-based auth.
Keys (request from the user on install)
Key generation (user-facing):
1. Log in to eToro. 2. Settings > Trading. 3. Create New Key. 4. Choose Environment (Real or Virtual/Demo) and Permissions (Read or Write). 5. Verify identity and copy the generated User Key.
Headers (every request):
x-request-id: unique UUID per requestx-api-key: Public API Key ()x-user-key: User Key ()Example:
curl -X GET "https://public-api.etoro.com/api/v1/watchlists" \
-H "x-request-id: " \
-H "x-api-key: " \
-H "x-user-key: "
Choosing the Auth Method in Code
When making requests, check which credentials are available:
if (ctx.accessToken) {
// SSO auth β Bearer token from OAuth token exchange
headers["Authorization"] = Bearer ${ctx.accessToken};
} else {
// Manual API key auth
headers["x-api-key"] = ctx.apiKey;
headers["x-user-key"] = ctx.userKey;
}
Request Conventions
/api/v1). GET /watchlists means GET https://public-api.etoro.com/api/v1/watchlists.
array, send them as comma-separated values (e.g., instrumentIds=1001,1002).pageNumber, pageSize
- People search & trade history: page, pageSize
- Feeds: take, offset
- Watchlist items listing: pageNumber, itemsPerPage
InstrumentID, IsBuy, Leverage).
- Market close body uses InstrumentId (capital I, lowercase d).
- Watchlist items use ItemId, ItemType, ItemRank.
- Feeds post body uses lower camel (owner, message, tags, mentions, attachments).
instrumentId vs InstrumentID). When extracting IDs, handle both if present.Demo vs Real Trading
/demo/) for testing and paper trading./trading/info/demo/*
- Real: /trading/info/portfolio and /trading/info/real/pnl
Use Defaults
Quick Start (Demo Trade)
1. Resolve instrumentId using search.
fields is required on search requests.
curl -X GET "https://public-api.etoro.com/api/v1/market-data/search?internalSymbolFull=BTC&fields=instrumentId,internalSymbolFull,displayname" \
-H "Authorization: Bearer " \
-H "x-request-id: "
2. Place a demo market order by amount (PascalCase body):
curl -X POST "https://public-api.etoro.com/api/v1/trading/execution/demo/market-open-orders/by-amount" \
-H "Authorization: Bearer " \
-H "x-request-id: " \
-H "Content-Type: application/json" \
-d '{
"InstrumentID": 100000,
"IsBuy": true,
"Leverage": 1,
"Amount": 100
}'
> Note: The examples above use OAuth (Bearer token). For API key auth, replace the Authorization header with x-api-key and x-user-key headers instead.
Common IDs
instrumentId: from Search or Instruments metadatapositionId: from Portfolio endpointsorderId: from execution responses or Portfolio endpointsmarketId: used by instrument feed endpoints (typically available in instrument metadata/search fields)userId: numeric eToro user ID (often referred to as CID in responses; discover via People endpoints/search)watchlistId: from watchlists list/create endpointsMarket Data (Requests)
Search instruments
GET /market-data/searchfields (comma-separated list of instrument fields to return)searchText, pageSize, pageNumber, sortinternalSymbolFull as a query param and verify the exact match.fields when you need IDs: include the instrument identifier (may appear as instrumentId or InstrumentID), plus internalSymbolFull and displayname (and marketId if you plan to use Feeds).Metadata
GET /market-data/instruments instrumentIds, exchangeIds, stocksIndustryIds, instrumentTypeIds.Prices & history
GET /market-data/instruments/rates instrumentIds (comma-separated).
GET /market-data/instruments/history/closing-price GET /market-data/instruments/{instrumentId}/history/candles/{direction}/{interval}/{candlesCount} direction: asc or desc. candlesCount max 1000.
Use only supported interval values (confirm via docs if unsure).Reference data
GET /market-data/exchanges (optional exchangeIds)GET /market-data/instrument-typesGET /market-data/stocks-industries (optional stocksIndustryIds)Trading Execution (Requests)
> Requires appropriate permissions (typically Write) and the correct environment (Demo vs Real).
Market Open Orders (by amount)
Endpoints:
POST /trading/execution/demo/market-open-orders/by-amountPOST /trading/execution/market-open-orders/by-amountBody (PascalCase, JSON):
InstrumentID, IsBuy, Leverage, AmountStopLossRate, TakeProfitRate, IsTslEnabled, IsNoStopLoss, IsNoTakeProfitMarket Open Orders (by units)
Endpoints:
POST /trading/execution/demo/market-open-orders/by-unitsPOST /trading/execution/market-open-orders/by-unitsBody (PascalCase, JSON):
InstrumentID, IsBuy, Leverage, AmountInUnitsStopLossRate, TakeProfitRate, IsTslEnabled, IsNoStopLoss, IsNoTakeProfitCancel Market Open Orders
Endpoints:
DELETE /trading/execution/demo/market-open-orders/{orderId}DELETE /trading/execution/market-open-orders/{orderId}Market Close Orders
Endpoints:
POST /trading/execution/demo/market-close-orders/positions/{positionId}POST /trading/execution/market-close-orders/positions/{positionId}DELETE /trading/execution/demo/market-close-orders/{orderId}DELETE /trading/execution/market-close-orders/{orderId}Body (JSON):
InstrumentIdUnitsToDeduct (number or null)Partial close: set UnitsToDeduct.
Full close: set UnitsToDeduct to null.
You must close by positionId, not by symbol.
Market-if-touched (Limit) Orders
Endpoints:
POST /trading/execution/demo/limit-ordersDELETE /trading/execution/demo/limit-orders/{orderId}POST /trading/execution/limit-ordersDELETE /trading/execution/limit-orders/{orderId}Body (PascalCase, JSON):
InstrumentID, IsBuy, Leverage, Rate, and one of Amount or AmountInUnitsStopLossRate, TakeProfitRate, IsTslEnabled, IsNoStopLoss, IsNoTakeProfitIsDiscounted, CIDTrading Info & Portfolio (Requests)
GET /trading/info/demo/pnlGET /trading/info/real/pnlGET /trading/info/demo/portfolioGET /trading/info/portfolio positionId and orderId for close/cancel flows.
GET /trading/info/trade/history minDate (YYYY-MM-DD). Optional: page, pageSize.Watchlists (Requests)
User watchlists
GET /watchlists itemsPerPageForSingle, ensureBuiltinWatchlists, addRelatedAssets.
GET /watchlists/{watchlistId} pageNumber, itemsPerPage.
POST /watchlists name (required), type, dynamicQuery (optional). (Uses query params, not a JSON body.)
PUT /watchlists/{watchlistId} newName (required). (Uses query params, not a JSON body.)
DELETE /watchlists/{watchlistId}Watchlist items (body schema)
WatchlistItemDto fields:
ItemId (required, int)ItemType (required, string: Instrument or Person)ItemRank (optional, int)Endpoints:
POST /watchlists/{watchlistId}/itemsPUT /watchlists/{watchlistId}/itemsDELETE /watchlists/{watchlistId}/itemsExample body:
[
{ "ItemId": 12345, "ItemType": "Instrument", "ItemRank": 1 },
{ "ItemId": 67890, "ItemType": "Instrument", "ItemRank": 2 }
]
Default watchlists
POST /watchlists/default-watchlist/selected-itemsGET /watchlists/default-watchlists/items itemsLimit, itemsPerPage.
POST /watchlists/newasdefault-watchlist name (required), type, dynamicQuery (optional).
PUT /watchlists/setUserSelectedUserDefault/{watchlistId}PUT /watchlists/rank/{watchlistId} newRank (required).Public watchlists
GET /watchlists/public/{userId}GET /watchlists/public/{userId}/{watchlistId}Feeds (Requests)
Read feeds
GET /feeds/instrument/{marketId} requesterUserId, take, offset, badgesExperimentIsEnabled, reactionsPageSize.
GET /feeds/user/{userId} requesterUserId, take, offset, badgesExperimentIsEnabled, reactionsPageSize.Notes:
marketId is associated with an instrument (typically available via instrument metadata/search if you include it in fields).userId is a numeric user identifier (CID). If you only have a username, discover the numeric ID via People endpoints (see User Info & Analytics).Create post
POST /feeds/postowner (int)
- message (string)
- tags: { "tags": [{ "name": "...", "id": "..." }] }
- mentions: { "mentions": [{ "userName": "...", "id": "...", "isD irect": true }] }
- attachments: array of objects with url, title, host, description, mediaType, and optional media.Minimal example:
{ "message": "Hello eToro feed!" }
Curated Lists & Recommendations (Requests)
GET /curated-listsGET /market-recommendations/{itemsCount}Popular Investors (Copiers)
GET /pi-data/copiersUser Info & Analytics (Requests)
GET /user-info/people usernames, cidList.
Use this to map username β CID (userId) when you need numeric userId for feeds/public watchlists.
GET /user-info/people/search period. Optional: page, pageSize, sort, popularInvestor, gainMax, maxDailyRiskScoreMin, maxDailyRiskScoreMax, maxMonthlyRiskScoreMin, maxMonthlyRiskScoreMax, weeksSinceRegistrationMin, countryId, instrumentId, instrumentPctMin, instrumentPctMax, isTestAccount, and other filters.
GET /user-info/people/{username}/gainGET /user-info/people/{username}/daily-gain minDate, maxDate, type (Daily or Period).
GET /user-info/people/{username}/portfolio/liveGET /user-info/people/{username}/tradeinfo period (e.g., LastTwoYears).Responses & Schemas
For response schemas and full examples, refer to:
https://api-portal.etoro.com/mcp