英文:
Yahoo Finance API - GET quotes returns "Invalid Cookie"
问题
在过去的几年里,我一直在使用以下链接获取查询中的符号的完整当前报价:
https://query1.finance.yahoo.com/v7/finance/quote?symbols=AAPL
突然之间,大约5小时前,我现在在每台设备上调用它时都会收到错误响应'未经授权 - 无效的Cookie'。有时,我会收到错误响应'未经授权 - 无效的Crumb'。
历史数据获取仍然正常:
(https://query1.finance.yahoo.com/v8/finance/chart/AAPL?metrics=high?&interval=1d&range=5d)。
是否有其他人遇到此问题或者知道我如何修复它?或者Yahoo是否在不告知任何人的情况下停止了此端点?
英文:
For the past few years, I've been using the following link to fetch a full current quote for the symbols in the query:
https://query1.finance.yahoo.com/v7/finance/quote?symbols=AAPL
All of a sudden, as of about 5 hours ago, I'm now getting the error response 'Unauthorised - Invalid Cookie' on every single device I call it from. Sometimes, I get the error response 'Unauthorised - Invalid Crumb'.
The historic data fetching still works fine:
(https://query1.finance.yahoo.com/v8/finance/chart/AAPL?metrics=high?&interval=1d&range=5d).
Is anyone else having this issue and/or knows how I can fix it??? Or have Yahoo discontinued this endpoint without telling anyone??
答案1
得分: 15
今天我遇到了同样的问题。在你的GET请求的URL中将“v7”更改为“v6”。这样至少现在可以正常工作。因为Yahoo Finance API自2017年以来并没有得到官方支持,所以我们尽力而为。
英文:
I had the same issue today. Change the "v7" to "v6" in the URL in your GET request. It will then work, at least for now. Because the Yahoo Finance API hasn't been officially supported since 2017, we do what we can with it.
答案2
得分: 9
答案3
得分: 6
我遇到了同样的问题,并切换到使用 https://query1.finance.yahoo.com/v8/finance/chart/{ticker}
终端点,似乎有效。响应格式略有不同,但似乎包含相似的信息。
例如,获取当前价格:
旧终端点:
quoteResponse.result[0].regularMarketPrice
新终端点:
chart.result[0].meta.regularMarketPrice
英文:
I came across the same problem and switched to using the https://query1.finance.yahoo.com/v8/finance/chart/{ticker}
endpoint which seems to work. The response format is slightly different but seems to contain similar information.
E.g. to get the current price:
Old endpoint:
quoteResponse.result[0].regularMarketPrice
New endpoint:
chart.result[0].meta.regularMarketPrice
答案4
得分: 4
如果您尝试访问引号 API 端点,例如:
https://query2.finance.yahoo.com/v7/finance/quote?symbols=AAL
您最终会收到以下响应:
{"finance":{"result":null,"error":{"code":"Unauthorized","description":"Invalid Crumb. For Developers - https://docs.google.com/forms/d/e/1FAIpQLSeyb7xMtZFjoNYI7XG1rjlhUopKAxdLAfSRcaPxg9p-9ii-_g/viewform?fbzx=-7189957683680596558"}}}(我替换了 bitly 链接以进行发布)
他们解释了如何禁用对报价数据的访问。
v6 端点已完全停用。
英文:
If you try to go to a quote api endpoint such as:
https://query2.finance.yahoo.com/v7/finance/quote?symbols=AAL
You end up getting this:
{"finance":{"result":null,"error":{"code":"Unauthorized","description":"Invalid Crumb. For Developers - https://docs.google.com/forms/d/e/1FAIpQLSeyb7xMtZFjoNYI7XG1rjlhUopKAxdLAfSRcaPxg9p-9ii-_g/viewform?fbzx=-7189957683680596558"}}} (i replaced the bitly link for posting)
Where they explain how they've disabled access to quote data.
The v6 endpoint was killed off entirely.
答案5
得分: 2
我刚刚检查过,v7现在又可以正常工作了。
英文:
I just checked and v7 is now working again.
答案6
得分: 1
The only option that I've found is to use a different service. I have a paid subscription to Financial Modeling Plus (https://site.financialmodelingprep.com/developer/docs/pricing/)
They have a free tier that provides 250 quotes a day. That might be adequate for some people that only want daily updates for their portfolio.
英文:
The only option that I've found is to use a different service. I have a paid subscription to Financial Modeling Plus (https://site.financialmodelingprep.com/developer/docs/pricing/)
They have a free tier that provides 250 quotes a day. That might be adequate for some people that only want daily updates for their portfolio.
答案7
得分: 1
An alternative I found on https://cryptocointracker.com/yahoo-finance/yahoo-finance-api#26cc3f3f62db4567857919c2653fab33 is to use https://query1.finance.yahoo.com/v7/finance/options/{symbol}
It has quote
value-pair containing:
{
"quote":{
"language":"en-US",
"region":"US",
"quoteType":"EQUITY",
"typeDisp":"Equity",
"quoteSourceName":"Delayed Quote",
"triggerable":true,
"customPriceAlertConfidence":"HIGH",
"exchange":"NMS",
"currency":"USD",
"earningsTimestamp":1691092800,
"earningsTimestampStart":1691092800,
"earningsTimestampEnd":1691092800,
"trailingAnnualDividendRate":0.92,
"trailingPE":32.131535,
"dividendRate":0.96,
"trailingAnnualDividendYield":0.004847974,
"dividendYield":0.51,
"epsTrailingTwelveMonths":5.93,
"epsForward":6.58,
"epsCurrentYear":5.98,
"priceEpsCurrentYear":31.862875,
"sharesOutstanding":15728700416,
"bookValue":3.953,
"fiftyDayAverage":180.4268,
"fiftyDayAverageChange":10.11319,
"fiftyDayAverageChangePercent":0.05605148,
"twoHundredDayAverage":156.0571,
"twoHundredDayAverageChange":34.482895,
"marketCap":2996946599936,
"twoHundredDayAverageChangePercent":0.22096333,
"forwardPE":28.957447,
"priceToBook":48.201363,
"sourceInterval":15,
"exchangeDataDelayedBy":0,
"averageAnalystRating":"2.0 - Buy",
"shortName":"Apple Inc.",
"longName":"Apple Inc.",
"messageBoardId":"finmb_24937",
"exchangeTimezoneName":"America/New_York",
"exchangeTimezoneShortName":"EDT",
"gmtOffSetMilliseconds":-14400000,
"market":"us_market",
"esgPopulated":false,
"marketState":"POSTPOST",
"regularMarketChangePercent":0.40574852,
"regularMarketPrice":190.54,
"tradeable":false,
"cryptoTradeable":false,
"firstTradeDateMilliseconds":345479400000,
"priceHint":2,
"postMarketChangePercent":-0.11545396,
"postMarketTime":1689292799,
"postMarketPrice":190.32,
"postMarketChange":-0.21998596,
"regularMarketChange":0.769989,
"regularMarketTime":1689278404,
"regularMarketDayHigh":191.19,
"regularMarketDayRange":"189.78 - 191.19",
"regularMarketDayLow":189.78,
"regularMarketVolume":38321242,
"regularMarketPreviousClose":189.77,
"bid":190.34,
"ask":190.38,
"bidSize":10,
"askSize":10,
"fullExchangeName":"NasdaqGS",
"financialCurrency":"USD",
"regularMarketOpen":190.5,
"averageDailyVolume3Month":56677663,
"averageDailyVolume10Day":51027070,
"fiftyTwoWeekLowChange":66.369995,
"fiftyTwoWeekLowChangePercent":0.5345091,
"fiftyTwoWeekRange":"124.17 - 194.48",
"fiftyTwoWeekHighChange":-3.9400024,
"fiftyTwoWeekHighChangePercent":-0.020259166,
"fiftyTwoWeekLow":124.17,
"fiftyTwoWeekHigh":194.48,
"fiftyTwoWeekChangePercent":26.882862,
"dividendDate":1684368000,
"displayName":"Apple",
"symbol":"AAPL"
}
}
英文:
An alternative I found on https://cryptocointracker.com/yahoo-finance/yahoo-finance-api#26cc3f3f62db4567857919c2653fab33 is to use https://query1.finance.yahoo.com/v7/finance/options/{symbol}
It has quote
value-pair containing:
{
"quote":{
"language":"en-US",
"region":"US",
"quoteType":"EQUITY",
"typeDisp":"Equity",
"quoteSourceName":"Delayed Quote",
"triggerable":true,
"customPriceAlertConfidence":"HIGH",
"exchange":"NMS",
"currency":"USD",
"earningsTimestamp":1691092800,
"earningsTimestampStart":1691092800,
"earningsTimestampEnd":1691092800,
"trailingAnnualDividendRate":0.92,
"trailingPE":32.131535,
"dividendRate":0.96,
"trailingAnnualDividendYield":0.004847974,
"dividendYield":0.51,
"epsTrailingTwelveMonths":5.93,
"epsForward":6.58,
"epsCurrentYear":5.98,
"priceEpsCurrentYear":31.862875,
"sharesOutstanding":15728700416,
"bookValue":3.953,
"fiftyDayAverage":180.4268,
"fiftyDayAverageChange":10.11319,
"fiftyDayAverageChangePercent":0.05605148,
"twoHundredDayAverage":156.0571,
"twoHundredDayAverageChange":34.482895,
"marketCap":2996946599936,
"twoHundredDayAverageChangePercent":0.22096333,
"forwardPE":28.957447,
"priceToBook":48.201363,
"sourceInterval":15,
"exchangeDataDelayedBy":0,
"averageAnalystRating":"2.0 - Buy",
"shortName":"Apple Inc.",
"longName":"Apple Inc.",
"messageBoardId":"finmb_24937",
"exchangeTimezoneName":"America/New_York",
"exchangeTimezoneShortName":"EDT",
"gmtOffSetMilliseconds":-14400000,
"market":"us_market",
"esgPopulated":false,
"marketState":"POSTPOST",
"regularMarketChangePercent":0.40574852,
"regularMarketPrice":190.54,
"tradeable":false,
"cryptoTradeable":false,
"firstTradeDateMilliseconds":345479400000,
"priceHint":2,
"postMarketChangePercent":-0.11545396,
"postMarketTime":1689292799,
"postMarketPrice":190.32,
"postMarketChange":-0.21998596,
"regularMarketChange":0.769989,
"regularMarketTime":1689278404,
"regularMarketDayHigh":191.19,
"regularMarketDayRange":"189.78 - 191.19",
"regularMarketDayLow":189.78,
"regularMarketVolume":38321242,
"regularMarketPreviousClose":189.77,
"bid":190.34,
"ask":190.38,
"bidSize":10,
"askSize":10,
"fullExchangeName":"NasdaqGS",
"financialCurrency":"USD",
"regularMarketOpen":190.5,
"averageDailyVolume3Month":56677663,
"averageDailyVolume10Day":51027070,
"fiftyTwoWeekLowChange":66.369995,
"fiftyTwoWeekLowChangePercent":0.5345091,
"fiftyTwoWeekRange":"124.17 - 194.48",
"fiftyTwoWeekHighChange":-3.9400024,
"fiftyTwoWeekHighChangePercent":-0.020259166,
"fiftyTwoWeekLow":124.17,
"fiftyTwoWeekHigh":194.48,
"fiftyTwoWeekChangePercent":26.882862,
"dividendDate":1684368000,
"displayName":"Apple",
"symbol":"AAPL"
}
}
答案8
得分: 0
这是我正在使用在一个.NET 7 API项目中的工作版本。我通过将cookie和crumb保存在缓存中来解决了问题,并在缓存过期时使用同步/重试机制。这对于一个服务器实例来说效果很好,但对于规模情况需要重新考虑。这是使用Yahoo V10的,没有问题。这里没有请求限制器,但我建议你使用一个。在我的情况下,我在UI中构建了一些内容(效率不是很高,但对我的情况足够好)。
using Microsoft.Extensions.Caching.Memory;
using System.Net;
namespace WorkbenchAPI.Features.ServiceDelivery.StockMarket.SymbolsParsers
{
public class YahooManager
{
// ... 代码继续 ...
}
}
请让我知道如果你需要更多信息。
英文:
This is my working version that I am using it in a .Net 7 API project. I've solved the problem with the cookie and crumb by keeping them in cache and I have a sync/retry mechanism when the cache expires. This works well for 1 server instance but for scale situation it should be rethink. This is using Yahoo V10 without problems. There is no requests limiter here but I advise you to use one. In my case I have something build in UI (not very efficient, but is good enough for my case).
using Microsoft.Extensions.Caching.Memory;
using System.Net;
namespace WorkbenchAPI.Features.ServiceDelivery.StockMarket.SymbolsParsers
{
public class YahooManager
{
private static readonly object yahooCredentialsLocker = new object();
private const string STOCK_MARKET_URL_SUMMARY = "https://query1.finance.yahoo.com/v10/finance/quoteSummary/{0}?formatted=true&lang=en-US&region=US&modules=assetProfile%2CbalanceSheetHistory%2CbalanceSheetHistoryQuarterly%2CcalendarEvents%2CcashflowStatementHistory%2CcashflowStatementHistoryQuarterly%2CdefaultKeyStatistics%2Cearnings%2CearningsHistory%2CearningsTrend%2CesgScores%2CfinancialData%2CfundOwnership%2CincomeStatementHistory%2CincomeStatementHistoryQuarterly%2CindexTrend%2CindustryTrend%2CinsiderHolders%2CinsiderTransactions%2CinstitutionOwnership%2CmajorDirectHolders%2CmajorHoldersBreakdown%2CnetSharePurchaseActivity%2Cprice%2CrecommendationTrend%2CsecFilings%2CsectorTrend%2CsummaryDetail%2CsummaryProfile%2CupgradeDowngradeHistory%2Cpageviews%2Cquotetype&ssl=true";
private const string YahooFcUrl = "https://fc.yahoo.com";
private const string YahooGetCrumbUrl = "https://query2.finance.yahoo.com/v1/test/getcrumb";
private const string CacheKeyCookieContainer = "YahooCookieContainer";
private const string CacheKeyCrumb = "YahooCrumb";
private readonly IHttpClientFactory _httpClientFactory;
private readonly IMemoryCache _memCache;
public YahooManager(IHttpClientFactory httpClientFactory, IMemoryCache memCache)
{
_httpClientFactory = httpClientFactory;
_memCache = memCache;
}
public async Task<string> GetYahooSymbolSummay(string symbol)
{
return await WebRequestGet(string.Format(STOCK_MARKET_URL_SUMMARY, symbol));
}
public async Task<string> WebRequestGet(string url)
{
(CookieContainer cookie, string crumb) credentials = new(null, null);
lock (yahooCredentialsLocker)
{
int tryCounter = 0;
while ((credentials.crumb == null || credentials.cookie == null) && tryCounter < 10)
{
Task.Delay(1000 * tryCounter).Wait();
credentials = GetYahooCookie().ConfigureAwait(false).GetAwaiter().GetResult();
tryCounter++;
}//while
}//lock
string sAgent = $"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36";
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url + "&crumb=" + credentials.crumb)
{
Headers =
{
{ "Cookie", credentials.cookie.GetCookieHeader(new Uri(YahooFcUrl))},
{ "User-Agent",sAgent }
}
};
var httpClient = _httpClientFactory.CreateClient();
var webResponse = await httpClient.SendAsync(httpRequestMessage);
//webResponse.EnsureSuccessStatusCode();
string responseContent = await webResponse.Content.ReadAsStringAsync();
return responseContent;
}//WebRequestGet
private async Task<(CookieContainer cookie, string crumb)> GetYahooCookie()
{
CookieContainer m_cookieContainer = null;
string m_crumb = null;
_memCache.TryGetValue(CacheKeyCookieContainer, out m_cookieContainer);
_memCache.TryGetValue(CacheKeyCrumb, out m_crumb);
if (m_cookieContainer == null || m_crumb == null)
{
string sAgent = $"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36";
m_cookieContainer = new CookieContainer();
var uri = new Uri(YahooFcUrl);
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, YahooFcUrl)
{
Headers =
{
{ "User-Agent", sAgent }
}
};
var httpClient = _httpClientFactory.CreateClient();
var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
IEnumerable<string> cookies;
httpResponseMessage.Headers.TryGetValues("Set-Cookie", out cookies);
if (cookies == null)
return (null, null);
foreach (var cookieValue in cookies)
m_cookieContainer.SetCookies(uri, cookieValue);
httpResponseMessage.Dispose();
httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, YahooGetCrumbUrl)
{
Headers =
{
{ "Cookie", m_cookieContainer.GetCookieHeader(uri) },
{ "User-Agent",sAgent}
}
};
httpClient = _httpClientFactory.CreateClient();
var crumbResponse = await httpClient.SendAsync(httpRequestMessage);
crumbResponse.EnsureSuccessStatusCode();
string responseContent = await crumbResponse.Content.ReadAsStringAsync();
m_crumb = responseContent.Trim('"');
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromSeconds(60*5))
.SetAbsoluteExpiration(TimeSpan.FromSeconds(60*10))
.SetPriority(CacheItemPriority.Normal);
_memCache.Set(CacheKeyCookieContainer, m_cookieContainer, cacheEntryOptions);
_memCache.Set(CacheKeyCrumb, m_crumb, cacheEntryOptions);
}//endif
return (m_cookieContainer, m_crumb);
}//GetYahooCookie
}
}
答案9
得分: -1
我怀疑工作中存在一些速率限制。如果您过快地发出太多调用,端点将把调用者视为自动程序而不是人类用户。在调用其中一个端点几千次后,我开始频繁收到“未经授权”的错误。等待几分钟后再次尝试,请求成功通过。
英文:
I suspect there are some rate limiting at work. If you are making too many calls too quickly, the endpoint will treat the caller as an automated program instead of a human users. I started getting a lot of Unauthorized
after calling one of the endpoints a few thousand times. Tried again after a few minutes and the request went through.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论