using System.Threading.Tasks;
var requests = new QuoteRequest[]
new DateTime(2004, 1, 1),
new DateTime(2019, 12, 31),
new DateTime(2004, 1, 1),
new DateTime(2019, 12, 31),
new DateTime(2004, 1, 1),
new DateTime(2019, 12, 31),
new DateTime(2004, 12, 1),
new DateTime(2019, 12, 31),
new DateTime(2004, 12, 1),
new DateTime(2019, 12, 31),
var crumb = await RevalidateCrumbAsync();
cookie = await RevalidateCookiesAsync();
foreach(var req in requests)
var csv = await DownloadQuoteFromYahooAsync(req.symbol, req.from, req.to, req.interval, crumb);
Console.WriteLine($"{csv.Length} bytes");
HttpClient newConfiguredHttpClient(bool useCookie = false)
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0");
if (useCookie && cookie.Length > 0)
httpClient.DefaultRequestHeaders.Add("Cookie", cookie);
httpClient.Timeout = TimeSpan.FromSeconds(15);
async Task<string> RevalidateCrumbAsync(string symbol = "GOOG")
var url = $"https://finance.yahoo.com/quote/{symbol}";
HttpClient httpClient = newConfiguredHttpClient();
var response = await httpClient.GetAsync(url);
var html = await response.Content.ReadAsStringAsync();
var pos = html.IndexOf("\"crumb\":\"");
var pos1 = html.IndexOf(":\"", pos) + 2;
var pos2 = html.IndexOf('"', pos1);
if (pos2 <= 0) return "";
return html.Substring(pos1, pos2 - pos1);
async Task<string> RevalidateCookiesAsync()
var url = "https://login.yahoo.com/";
HttpClient httpClient = newConfiguredHttpClient();
var response = await httpClient.GetAsync(url);
foreach (var header in response.Headers)
if (header.Key != "Set-Cookie") continue;
foreach (var cookie in header.Value)
var pos = cookie.IndexOf(";");
cookies += cookie.Substring(0, pos);
async Task<string> DownloadQuoteFromYahooAsync(string symbol, DateTime startDate, DateTime endDate, string interval, string crumb = "")
var period1 = (int)(startDate.Subtract(new DateTime(1970, 1, 1)).TotalSeconds);
var period2 = (int)(endDate.Subtract(new DateTime(1970, 1, 1)).TotalSeconds);
var url = $"https://query1.finance.yahoo.com/v7/finance/download/{symbol}?period1={period1}&period2={period2}&interval={interval}&events=history";
url += "&crumb=" + crumb;
HttpClient httpClient = newConfiguredHttpClient(true);
var response = await httpClient.GetAsync(url).ConfigureAwait(false);
Console.WriteLine($"status = {response.StatusCode}");
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
internal record QuoteRequest(string symbol, DateTime from, DateTime to, string interval);