/bin/scripts/purge_perplexity.js
Updated September 11, 2026: Perplexity changed the internals this tool relies on, and the old version stopped working. The source code and bookmarklet below are working again. Sorry for the delay in getting this fixed.
I love Perplexity. It has effectively replaced Google for me. But my history is a graveyard of half-baked thoughts, random debugging queries, and "how to center a div" searches that I don't need to see again. So I created a bookmarklet that deletes your saved Perplexity session history—including pinned sessions. Copy the updated code below, or read on to learn how it works.
The Tool
Option 1: The Bookmarklet
Fast, one‑click cleanup once it's set up.
- Log in to perplexity.ai and click Copy Bookmarklet Code.
- Create a new bookmark and paste the code into the URL field.
- Name it (for example Clear Perplexity) and click it while on perplexity.ai.
Option 2: The Source Code
For those who prefer to see everything. You get the full, unminified script that the bookmarklet runs.
- Open your browser's developer console (F12).
- Click Copy Source Code to copy the script.
- Paste it into the console and press Enter.
- Follow the on‑screen prompts.
Both options contain the same updated cleaner. If your browser strips the javascript: prefix from the bookmark, use Copy Source Code and run it directly in the console instead.
The Discovery
The cleaner uses the same authenticated internal endpoints as Perplexity's web app. The 2026 update now warms the logged-in session, discovers both regular and pinned sessions, deduplicates them, and supports both entry and context UUIDs before deleting anything.
The modal still shows the total first and asks for confirmation. Deletions run in measured batches, report partial failures instead of pretending everything succeeded, and refresh the page when the cleanup completes.
Under the Hood
For the developers out there, here is how it works.
The Constants & Setup
Perplexity includes an API version in its own network requests. The updated script discovers that version from the browser's resource timing entries and falls back to 2.18 when none is available. Scanning and deletion use separate batch sizes so each can be tuned safely.
const API_VERSION = (() => {
const patterns = [
"/rest/user/info",
"/rest/thread/list_ask_threads",
"/rest/thread/list_pinned_ask_threads",
"/rest/sidebar",
];
for (const entry of performance.getEntriesByType("resource")) {
if (!patterns.some((pattern) => entry.name.includes(pattern))) continue;
const version = new URL(entry.name).searchParams.get("version");
if (version) return version;
}
return "2.18";
})();
const PAGE_SIZE = 50;
const DELETE_BATCH_SIZE = 64;
const DELETE_DELAY_MS = 800;
const SCAN_RETRIES = 3;
The UI Injection
Since we're running in the console context, we can't rely on React or the existing page structure. We have to inject our own UI. I created a self-contained overlay using standard DOM APIs and injected a style block to make it match the site's dark theme.
function createOverlay() {
// ... removal of existing overlay ...
overlay = document.createElement('div');
overlay.id = 'perplexity-cleaner-overlay';
// ... innerHTML construction for modal ...
const style = document.createElement('style');
style.textContent = `#perplexity-cleaner-overlay { ... }`; // CSS styles
document.head.appendChild(style);
document.body.appendChild(overlay);
// ... grabbing references to elements ...
}
The API Client
This is the heavy lifter. Requests are built against the current page origin, include the logged-in browser's credentials, and send the app/version headers Perplexity expects. The updated version also parses useful API error details instead of reducing every failure to a status code.
async function apiCall(path, method, body, reason) {
const url = new URL(path, location.origin);
url.searchParams.set("version", API_VERSION);
url.searchParams.set("source", "default");
const response = await fetch(url, {
method,
credentials: "include",
headers: {
"Content-Type": "application/json",
"X-App-ApiClient": "default",
"X-App-ApiVersion": API_VERSION,
"X-Perplexity-Request-Endpoint": url.toString(),
"X-Perplexity-Request-Reason": reason,
},
body: body == null ? undefined : JSON.stringify(body),
});
// Parse the response and surface any useful error details.
// See the full source for the complete implementation.
}
The Discovery Loop
Perplexity paginates regular sessions and exposes pinned sessions separately. The cleaner warms the authentication session, fetches both lists, retries an initially empty response, then deduplicates records by uuid or context_uuid.
async function fetchAllThreads() {
await warmSession();
const [regular, pinned] = await Promise.all([
fetchRegularThreads(),
fetchPinnedThreads(),
]);
const merged = [];
const seen = new Set();
for (const thread of [...pinned, ...regular]) {
const key = thread.uuid || thread.context_uuid;
if (!key || seen.has(key)) continue;
seen.add(key);
merged.push(thread);
}
return merged;
}
The Deletion Logic
Once we have the identifiers, we convert them into deletion targets and batch them into groups of 64. Each request can include both entry_uuids and context_uuids. An 800ms pause between batches avoids hammering the endpoint, while per-item failure accounting keeps the final result honest.
async function deleteThreads(targets) {
let deleted = 0;
let failed = 0;
for (let i = 0; i < targets.length; i += DELETE_BATCH_SIZE) {
const batch = targets.slice(i, i + DELETE_BATCH_SIZE);
const result = await deleteThreadBatch(batch);
const batchFailures = countBatchFailures(result, batch);
failed += batchFailures;
deleted += batch.length - batchFailures;
if (i + DELETE_BATCH_SIZE < targets.length) {
await wait(DELETE_DELAY_MS);
}
}
}
I will be publishing more useful tools like this in the future, hope this helps someone.
Warning
This script is a blunt instrument. It does not ask "are you sure?" for individual threads. Once you confirm the total count, it deletes everything. There is no undo button, no trash can, and no recovery.
Use it wisely.
<terminate_session />