curl --request GET \
--url https://public-api.etoro.com/api/v1/data/instruments/{instrumentId}/candles \
--header 'x-api-key: <api-key>' \
--header 'x-request-id: <x-request-id>' \
--header 'x-user-key: <api-key>'import requests
url = "https://public-api.etoro.com/api/v1/data/instruments/{instrumentId}/candles"
headers = {
"x-request-id": "<x-request-id>",
"x-api-key": "<api-key>",
"x-user-key": "<api-key>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {
'x-request-id': '<x-request-id>',
'x-api-key': '<api-key>',
'x-user-key': '<api-key>'
}
};
fetch('https://public-api.etoro.com/api/v1/data/instruments/{instrumentId}/candles', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://public-api.etoro.com/api/v1/data/instruments/{instrumentId}/candles",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: <api-key>",
"x-request-id: <x-request-id>",
"x-user-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://public-api.etoro.com/api/v1/data/instruments/{instrumentId}/candles"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-request-id", "<x-request-id>")
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("x-user-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://public-api.etoro.com/api/v1/data/instruments/{instrumentId}/candles")
.header("x-request-id", "<x-request-id>")
.header("x-api-key", "<api-key>")
.header("x-user-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://public-api.etoro.com/api/v1/data/instruments/{instrumentId}/candles")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-request-id"] = '<x-request-id>'
request["x-api-key"] = '<api-key>'
request["x-user-key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"instrumentId": 1001,
"symbol": "AAPL",
"interval": "1d",
"side": "bid",
"window": {
"from": "2026-09-01T00:00:00Z",
"to": "2026-09-28T00:00:00Z"
},
"pagination": {
"limit": 100,
"hasNext": false,
"nextCursor": null
},
"results": [
{
"time": "2026-09-27T21:00:00Z",
"open": "228.5",
"high": "231.2",
"low": "227.9",
"close": "230.1",
"volume": "1234567"
}
]
}{
"success": false,
"error": {
"code": "INVALID_TIME_RANGE",
"message": "`from` must be earlier than `to`"
}
}{
"success": false,
"error": {
"code": "DEPENDENCY_UNAVAILABLE",
"message": "The requested resource is temporarily unavailable"
}
}Get price candles for an instrument
Rate limit: 120 requests per 60 seconds. This is a shared quota — the same budget is consumed by a group of related endpoints, so calling any of them reduces what is left for the others (you cannot call each at the full rate independently). Endpoints sharing this quota:
GET /api/v1/data/instruments/{instrumentId}/candles/coverage
Full-history OHLC candles for one instrument and interval, from a public view built and kept current by a DataPlatform job. Not user-scoped: identical for every caller for the same instrument and parameters. A range without from returns the most recent slice; a slice covers at most 10,080 periods of the interval (7 days of 1m, up to all history for 1d/1w), and a longer range is paged with the keyset cursor in pagination.nextCursor. An instrument with no data for the interval, or one eToro shows internally only, returns 200 with an empty results array, not 404. Call the coverage endpoint first to find a range that has data.
curl --request GET \
--url https://public-api.etoro.com/api/v1/data/instruments/{instrumentId}/candles \
--header 'x-api-key: <api-key>' \
--header 'x-request-id: <x-request-id>' \
--header 'x-user-key: <api-key>'import requests
url = "https://public-api.etoro.com/api/v1/data/instruments/{instrumentId}/candles"
headers = {
"x-request-id": "<x-request-id>",
"x-api-key": "<api-key>",
"x-user-key": "<api-key>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {
'x-request-id': '<x-request-id>',
'x-api-key': '<api-key>',
'x-user-key': '<api-key>'
}
};
fetch('https://public-api.etoro.com/api/v1/data/instruments/{instrumentId}/candles', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://public-api.etoro.com/api/v1/data/instruments/{instrumentId}/candles",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: <api-key>",
"x-request-id: <x-request-id>",
"x-user-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://public-api.etoro.com/api/v1/data/instruments/{instrumentId}/candles"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-request-id", "<x-request-id>")
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("x-user-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://public-api.etoro.com/api/v1/data/instruments/{instrumentId}/candles")
.header("x-request-id", "<x-request-id>")
.header("x-api-key", "<api-key>")
.header("x-user-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://public-api.etoro.com/api/v1/data/instruments/{instrumentId}/candles")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-request-id"] = '<x-request-id>'
request["x-api-key"] = '<api-key>'
request["x-user-key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"instrumentId": 1001,
"symbol": "AAPL",
"interval": "1d",
"side": "bid",
"window": {
"from": "2026-09-01T00:00:00Z",
"to": "2026-09-28T00:00:00Z"
},
"pagination": {
"limit": 100,
"hasNext": false,
"nextCursor": null
},
"results": [
{
"time": "2026-09-27T21:00:00Z",
"open": "228.5",
"high": "231.2",
"low": "227.9",
"close": "230.1",
"volume": "1234567"
}
]
}{
"success": false,
"error": {
"code": "INVALID_TIME_RANGE",
"message": "`from` must be earlier than `to`"
}
}{
"success": false,
"error": {
"code": "DEPENDENCY_UNAVAILABLE",
"message": "The requested resource is temporarily unavailable"
}
}Authorizations
API key of the application. Only valid together with the x-user-key header — the pair is an alternative to OAuth bearer authentication, never sent alongside it. The pair is granted the same permissions the operation's OAuth scopes describe.
Demo credential for trying the API from these docs: lhgfaslk21490FAScVPkdsb53F9dNkfHG4faZSG5vfjndfcfgdssdgsdHF4663
User-specific authentication key. Only valid together with the x-api-key header — the pair is an alternative to OAuth bearer authentication, never sent alongside it.
Demo credential for trying the API from these docs: eyJlYW4iOiJVbnJlZ2lzdGVyZWRBcHBsaWNhdGlvbiIsImVrIjoiOE5sZ2cwcW5EUVdROUFNWGpXT2lmOWktZnpidG5KcUlqWGJ3WHJZZkpZcldrbG90ZEhvLVBjSWhQaU8xU1ZtMW84aU1WZGZqN2xWNzFjLXFxLmcybXE1dnh4Q1hUT25xaWRUaTFlcEhmVk1fIn0_
Headers
A unique request identifier.
"e0245e0b-362b-4f73-85e7-43a7cfb2a9e9"
Path Parameters
eToro instrument id, e.g. 1001 for AAPL.
x >= 1Query Parameters
Candle interval.
1m, 5m, 10m, 15m, 30m, 1h, 4h, 1d, 1w Inclusive lower bound on candle open time, ISO 8601 with a time zone. Defaults to one slice before to.
Exclusive upper bound on candle open time, ISO 8601 with a time zone. Defaults to now.
Candles per page. Defaults to 100, maximum 2000.
1 <= x <= 2000Price side to return. Defaults to bid.
bid, ask, both compact names each column once; results becomes one array per candle instead of an object.
verbose, compact Opaque token from pagination.nextCursor.
Response
One page of candles for the requested instrument and interval, newest first unless walking an older slice.
One page of price candles for a single instrument and interval.
Instrument display name, e.g. AAPL/USD. Null when the page is empty.
1m, 5m, 10m, 15m, 30m, 1h, 4h, 1d, 1w bid, ask, both The requested time range, after defaults: [from, to).
Show child attributes
Show child attributes
Keyset pagination metadata for a time-ordered collection. hasNext is always present; nextCursor is opaque and is null exactly when hasNext is false.
Show child attributes
Show child attributes
Show child attributes
Show child attributes