Check Website Uptime


To see whether a website is responding, make a request with puter.net.fetch() and save the result with puter.kv.set(). This gives your app a history of successful checks, HTTP errors and connection failures.

This example checks a site when the user clicks a button, keeps each result for seven days, and shows the latest 20 checks. Each user's history lives in their own account. The first check opens the Puter sign-in window if the user isn't signed in yet.

Check the Site

Use a HEAD request to read the site's status without downloading its page. Check response.ok to tell whether it returned a status from 200 to 299, and catch request failures so they can be saved too:

async function checkWebsite(url) {
    const started = performance.now();
    const result = {
        at: Date.now(),
        up: false,
        status: null,
        durationMs: 0,
        error: null,
    };

    try {
        const response = await puter.net.fetch(url, { method: 'HEAD' });
        result.up = response.ok;
        result.status = response.status;
    } catch (error) {
        result.error = String(error?.message ?? error?.error?.message ?? error);
    }
    result.durationMs = Math.round(performance.now() - started);

    const prefix = `uptime:${encodeURIComponent(url)}:`;
    const key = `${prefix}${new Date(result.at).toISOString()}:${crypto.randomUUID()}`;
    const inSevenDays = Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60;
    await puter.kv.set(key, result, inSevenDays);
    return result;
}

An HTTP error has a status, such as 503. A request that fails before getting a response has status: null and an error message. A failed KV write still rejects, so the app can tell the user when a result couldn't be saved.

This example treats every status outside 200 to 299 as down. Change that rule if your endpoint uses another status for a successful check. Use an endpoint that accepts HEAD, because a site that rejects it can return 405 even when its pages work. For a GET check, cancel the response body with await response.body?.cancel() after reading the status.

The elapsed time includes connection setup and the network path from the user's browser, so each result is one sample of the site's availability.

Each check has its own key. The timestamp sorts checks by time, and the UUID keeps two checks made in the same millisecond from replacing each other. The third argument to puter.kv.set() is an expiry timestamp in seconds.

Read Recent Checks

List the site's keys with puter.kv.list(). Pass reverse: true to put the newest first and limit: 20 to read one page of history:

async function getHistory(url) {
    const page = await puter.kv.list({
        pattern: `uptime:${encodeURIComponent(url)}:*`,
        returnValues: true,
        reverse: true,
        limit: 20,
        fetchUntilFull: true,
    });

    return page.items.map((entry) => entry.value);
}

With limit, puter.kv.list() returns a page object whose items hold the key-value pairs. The fetchUntilFull option fills the page when possible, even if expired keys are skipped. A site with no saved checks returns an empty list.

To show older results, keep the page's cursor and request the next page, as in store a large collection.

Show the History

Add a button, a status message and a place for the history to a page that loads Puter.js:

<button id="check-site" type="button">Check now</button>
<p id="check-status" role="status"></p>
<pre id="check-history"></pre>

Put the two functions above and this code in the same script, after the elements. Change targetUrl to the site or health endpoint you want to check:

const targetUrl = 'https://example.com/';
const checkButton = document.getElementById('check-site');
const status = document.getElementById('check-status');
const history = document.getElementById('check-history');

async function showHistory() {
    const checks = await getHistory(targetUrl);
    history.textContent = checks.length ? checks.map((check) => {
        const time = new Date(check.at).toLocaleString();
        const outcome = check.up ? 'Up' : 'Down';
        const detail = check.status === null ? check.error : `HTTP ${check.status}`;
        return `${time} - ${outcome} - ${detail} - ${check.durationMs} ms`;
    }).join('\n') : 'No checks yet.';
}

checkButton.addEventListener('click', async () => {
    checkButton.disabled = true;
    status.textContent = 'Checking...';

    try {
        const result = await checkWebsite(targetUrl);
        status.textContent = result.up ? 'The site responded successfully.' : 'The check failed.';
        await showHistory();
    } catch (error) {
        const message = error?.message ?? error?.error?.message ?? error;
        status.textContent = `Could not save or load the results: ${message}`;
    } finally {
        checkButton.disabled = false;
    }
});

showHistory().catch((error) => {
    const message = error?.message ?? error?.error?.message ?? error;
    status.textContent = `Could not load the history: ${message}`;
});

The button stays disabled until the check and history refresh finish. A connection failure is saved as a failed check, and its message appears in the history. Checks run only when the user clicks the button, so closing the page stops them.

← All recipes