Skip to content

Instantly share code, notes, and snippets.

@robere2
Last active March 14, 2025 02:28
Show Gist options
  • Save robere2/232691590952b1f1c3de1993b291b6eb to your computer and use it in GitHub Desktop.
Save robere2/232691590952b1f1c3de1993b291b6eb to your computer and use it in GitHub Desktop.
Fetching auction data from the Hypixel API

Requesting Hypixel SkyBlock Auction Data

The Hypixel API splits up SkyBlock auction data into pages in order to keep responses small. If you want to fetch all SkyBlock auctions, you need to iterate over every page in the API, sending dozens of API requests simultaneously. Auction data is cached by Hypixel for 60 seconds before it is refreshed. When you send your requests, not all of them are going to finish at the same time. If you're unlucky, some requests may finish before the refresh, while others will finish after. Best case, this will result in your collection of auctions having missing, incorrect, or duplicate data. Worst case, you could receive API response errors about missing pages or unavailable data mid-refresh. You should be prepared for this in your code, and restart from page zero if something goes wrong. This is a JavaScript implementation of this idea.

Tip

Please include the proper license disclosures in your code. Keeping the documentation with the @license and @author annotations in place is all you need. If you are referencing this code while writing your own implementation, add this information yourself, and consider linking back to this Gist.

Usage

Two functions are available:

  • fetchAllAuctionPages will fetch every page from the auctions API and return each page as an entry in an array. In the event of a refresh or other anomaly, it will restart, up to 5 times.
  • fetchAuctionPage will fetch a single page and will simply check that the response is valid.

For more information about each function, read the documentation annotating each function in the code.

If you want to import these functions into another file, export everything by adding the following to the end of this file:

export { AuctionsApiError, fetchAuctionPage, fetchAllAuctionPages };
/*
Copyright 2025 https://github.com/robere2
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the “Software”), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
/**
* @license MIT
* @author https://github.com/robere2
* @see {@link https://gist.github.com/robere2/232691590952b1f1c3de1993b291b6eb} Source
*/
class AuctionApiError extends Error {
constructor(message, options) {
super(message, options);
this.name = "AuctionApiError";
}
}
/**
* Request an auction page from https://api.hypixel.net/v2/skyblock/auctions and verify that the API responded with 200 and "success" equal to true.
* @param {number} pageNum Page to request. Must be an integer greater than or equal to 0.
* @param {AbortSignal|undefined} signal Optional AbortController signal to cancel the request
* @returns {Promise<Object, Error>} Parsed API auction data from the response
* @reject {AuctionApiError} if the response code is not 200, if the body is not JSON, or if the "success" property on the body's JSON is not true.
* @reject {AbortError} if you pass an AbortSignal as the second parameter and fire that signal before the request can finish.
* @reject {TypeError} Network error
* @license MIT
* @author https://github.com/robere2
* @see {@link https://gist.github.com/robere2/232691590952b1f1c3de1993b291b6eb} Source
* @see {@link https://api.hypixel.net/#tag/SkyBlock/paths/~1v2~1skyblock~1auction/get} Hypixel auction API documentation
*/
async function fetchAuctionPage(pageNum, signal) {
pageNum = Math.max(0, pageNum);
pageNum = Math.round(pageNum);
const res = await fetch(`https://api.hypixel.net/v2/skyblock/auctions?page=${pageNum}`, {
signal
});
if(res.status !== 200) {
throw new AuctionApiError("Auctions API status code not equal to 200");
}
let parsed;
try {
parsed = await res.json();
} catch (e) {
throw new AuctionApiError("Auction API response body is not valid JSON");
}
if(parsed.success !== true) {
throw new AuctionApiError("Auction API status 'success' is not equal to true.");
}
return parsed;
}
/**
* Robust function to fetch all auction pages from https://api.hypixel.net/v2/skyblock/auctions. If the auctions update during the time that
* this function is executing, then it will restart in order to fetch the latest data.
* @returns {Promise<Object[], Error>} Array of JSON responses from the API, with each entry being a page of auction results. The results are not necessarily in order.
* @reject {TypeError} Network error
* @reject {AuctionApiError} If the function restarts more than five times due to updates or an API response with "success" set to false.
* @license MIT
* @author https://github.com/robere2
* @see {@link https://gist.github.com/robere2/232691590952b1f1c3de1993b291b6eb} Source
* @see {@link https://api.hypixel.net/#tag/SkyBlock/paths/~1v2~1skyblock~1auction/get} Hypixel auction API documentation
*/
async function fetchAllAuctionPages() {
let allPages = []
let tries = 0;
while(allPages.length === 0) {
// After five failed attempts, presumably more attempts aren't going to solve the problem.
if(tries++ >= 5) {
throw new AuctionApiError("Tried to fetch auction results five times, but all attempts failed. This could be a Hypixel API error, " +
"or responses are not coming in fast enough to keep up with the updating results.")
}
// On failure, give the API a moment to resolve itself and then retry.
let firstPage;
try {
firstPage = await fetchAuctionPage(0);
} catch(e) {
await new Promise(resolve => setTimeout(() => resolve(), 1000));
continue;
}
// Last updated timestamp is used to restart the process if results update between now and when we finish all page requests
const lastUpdated = firstPage.lastUpdated;
const abortController = new AbortController();
// Request each following page concurrently
const promises = [];
for(let i = 1; i < firstPage.totalPages; i++) {
promises.push((async () => {
try {
const page = await fetchAuctionPage(i, abortController.signal);
if(page.lastUpdated !== lastUpdated) {
throw new AuctionApiError("Auction list has updated while fetching all auctions")
}
return page;
} catch(e) {
if(e.name === "AuctionApiError") {
// Abort and restart if the results have updated or API says request failed. Wait a moment to give the
// error some time to resolve itself.
abortController.abort();
await new Promise(resolve => setTimeout(() => resolve(), 1000));
} else if(e.name !== "AbortError") {
throw e;
}
}
})())
}
// Await all of the page requests to complete, and add their responses to an array with the first page
allPages = [firstPage, ...await Promise.all(promises)]
// Aborted due to failure or updated list, restart by deleting the responses
if(abortController.signal.aborted) {
allPages = [];
}
}
return allPages;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment