Plain basic /prices wrapper

make_api <- function(base) {
  return(function(path) {
    url <- httr::modify_url(base, path = "prices")
    resp <- httr::GET(url) # this will be POST with JSON body
    
    if(resp$status_code != 200) {
      stop(paste("API Responded with", resp$status_code, "status code", sep = " "), 
           call. = FALSE)
    }
    if(httr::http_type(resp) != 'application/json') {
      stop(paste("API Responded with", http_type(resp), "content type", sep = " "), 
           call. = FALSE)
    }
    
    res <- jsonlite::fromJSON(httr::content(resp, "text", encoding = "UTF-8"))
    
    res$meta$from <- as.POSIXct(res$meta$from)
    res$meta$to <- as.POSIXct(res$meta$to)
    res$data$time <- as.POSIXct(res$data$time)
    res$data <- tibble::as_tibble(res$data)
    
    return(res)
  })
}

Instantiate prices api and fetch prices

api <- make_api("http://localhost:5000")
prices <- api("prices")

Response contains meta:

str(prices$meta)
List of 5
 $ period    :List of 2
  ..$ name    : chr "one_hour"
  ..$ interval: int 3600000
 $ side      : chr "ask"
 $ from      : POSIXct[1:1], format: "2020-03-04 11:00:00"
 $ instrument: chr "EUR/USD"
 $ to        : POSIXct[1:1], format: "2020-03-08 15:00:00"

And prices:

str(prices$data)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   100 obs. of  6 variables:
 $ volume: num  11118 11778 8488 11573 11489 ...
 $ high  : num  1.12 1.12 1.12 1.11 1.11 ...
 $ low   : num  1.12 1.11 1.11 1.11 1.11 ...
 $ time  : POSIXct, format: "2020-03-04 11:00:00" "2020-03-04 12:00:00" "2020-03-04 13:00:00" "2020-03-04 14:00:00" ...
 $ close : num  1.12 1.11 1.11 1.11 1.11 ...
 $ open  : num  1.12 1.12 1.11 1.11 1.11 ...

Plot prices

library(ggplot2)
theme_set(theme_minimal())

ggplot(data = prices$data, aes(x = time)) + 
  geom_line(aes(y = open), alpha = 0.5) +
  geom_line(aes(y = close), alpha = 0.5) +
  geom_line(aes(y = (open + close) / 2), color = 'red')

LS0tCnRpdGxlOiAiUHJpY2VzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAotLS0KUGxhaW4gYmFzaWMgYC9wcmljZXNgIHdyYXBwZXIKCmBgYHtyfQptYWtlX2FwaSA8LSBmdW5jdGlvbihiYXNlKSB7CiAgcmV0dXJuKGZ1bmN0aW9uKHBhdGgpIHsKICAgIHVybCA8LSBodHRyOjptb2RpZnlfdXJsKGJhc2UsIHBhdGggPSAicHJpY2VzIikKICAgIHJlc3AgPC0gaHR0cjo6R0VUKHVybCkgIyB0aGlzIHdpbGwgYmUgUE9TVCB3aXRoIEpTT04gYm9keQogICAgCiAgICBpZihyZXNwJHN0YXR1c19jb2RlICE9IDIwMCkgewogICAgICBzdG9wKHBhc3RlKCJBUEkgUmVzcG9uZGVkIHdpdGgiLCByZXNwJHN0YXR1c19jb2RlLCAic3RhdHVzIGNvZGUiLCBzZXAgPSAiICIpLCAKICAgICAgICAgICBjYWxsLiA9IEZBTFNFKQogICAgfQogICAgaWYoaHR0cjo6aHR0cF90eXBlKHJlc3ApICE9ICdhcHBsaWNhdGlvbi9qc29uJykgewogICAgICBzdG9wKHBhc3RlKCJBUEkgUmVzcG9uZGVkIHdpdGgiLCBodHRwX3R5cGUocmVzcCksICJjb250ZW50IHR5cGUiLCBzZXAgPSAiICIpLCAKICAgICAgICAgICBjYWxsLiA9IEZBTFNFKQogICAgfQogICAgCiAgICByZXMgPC0ganNvbmxpdGU6OmZyb21KU09OKGh0dHI6OmNvbnRlbnQocmVzcCwgInRleHQiLCBlbmNvZGluZyA9ICJVVEYtOCIpKQogICAgCiAgICByZXMkbWV0YSRmcm9tIDwtIGFzLlBPU0lYY3QocmVzJG1ldGEkZnJvbSkKICAgIHJlcyRtZXRhJHRvIDwtIGFzLlBPU0lYY3QocmVzJG1ldGEkdG8pCiAgICByZXMkZGF0YSR0aW1lIDwtIGFzLlBPU0lYY3QocmVzJGRhdGEkdGltZSkKICAgIHJlcyRkYXRhIDwtIHRpYmJsZTo6YXNfdGliYmxlKHJlcyRkYXRhKQogICAgCiAgICByZXR1cm4ocmVzKQogIH0pCn0KYGBgCgpJbnN0YW50aWF0ZSBwcmljZXMgYXBpIGFuZCBmZXRjaCBwcmljZXMKCmBgYHtyfQphcGkgPC0gbWFrZV9hcGkoImh0dHA6Ly9sb2NhbGhvc3Q6NTAwMCIpCnByaWNlcyA8LSBhcGkoInByaWNlcyIpCmBgYAoKUmVzcG9uc2UgY29udGFpbnMgbWV0YToKCmBgYHtyfQpzdHIocHJpY2VzJG1ldGEpCmBgYAoKQW5kIHByaWNlczoKCmBgYHtyfQpzdHIocHJpY2VzJGRhdGEpCmBgYAoKClBsb3QgcHJpY2VzCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKQoKZ2dwbG90KGRhdGEgPSBwcmljZXMkZGF0YSwgYWVzKHggPSB0aW1lKSkgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSBvcGVuKSwgYWxwaGEgPSAwLjUpICsKICBnZW9tX2xpbmUoYWVzKHkgPSBjbG9zZSksIGFscGhhID0gMC41KSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gKG9wZW4gKyBjbG9zZSkgLyAyKSwgY29sb3IgPSAncmVkJykKYGBgCg==