BlurShot API Documentation
Integration guide for developers building extensions and integrations with BlurShot's privacy-first screenshot editing capabilities.
Overview
BlurShot is designed as a privacy-first, client-side application. All image processing happens in the browser using HTML5 Canvas APIs. There is no backend API for processing images, as this would violate our core privacy principles.
However, BlurShot provides several integration points for browser extensions, automation tools, and other applications that want to leverage our client-side processing capabilities.
Teams that need rapid annotation before blurring often pair their flows with AnnotateFast, one of our privacy-first partners focused on screenshot markup.
JavaScript Integration
🔧 Browser Extension Integration
BlurShot can be embedded in browser extensions to provide screenshot editing capabilities directly in your extension's popup or side panel.
Creating Share Links Programmatically
// Share payload structure used by BlurShot
const shareState = {
i: imageDataBase64, // image data (without the data:image/png;base64, prefix)
n: 'screenshot.png', // optional filename
c: 12 // optional edit count metadata
};
const encoded = btoa(JSON.stringify(shareState));
const shareUrl = `https://blurshot.io/#s=${encodeURIComponent(encoded)}`;
window.location.href = shareUrl; // or copy to clipboard// Decoding a BlurShot share link
const url = new URL('https://blurshot.io/#s=eyJpIjoiUE5H...');
const payload = url.hash.slice(3); // remove '#s='
const json = atob(decodeURIComponent(payload));
const state = JSON.parse(json);
const imageDataUrl = `data:image/png;base64,${state.i}`;
// Use the data URL however you like (download, store, etc.)Share Payload Limits
- Max encoded image size: ~5 MB (after stripping the
data:image/*;base64,prefix). - Max URL/hash length: ~250 KB. Browsers may truncate longer URLs.
- If either limit is exceeded the UI surfaces a fallback alert instructing the user to download instead.
Restoring Shared Edits
To load an edit on another device, open the #s= link shown after sharing. The legacy ?edit= query only works on the same browser where the IndexedDB history was created.
// Open a share link in a new tab
const shareUrl = 'https://blurshot.io/#s=eyJpIjoiUE5H...';
window.open(shareUrl, '_blank');
// Legacy (same-device only)
window.location.href = 'https://blurshot.io/?edit=42';URL Parameters
| Parameter | Description | Example |
|---|---|---|
| #s | Hash fragment containing the share payload (base64 JSON with i, n, c fields). Preferred method for cross-device restores. | #s=eyJpIjoiUE5H... |
| edit (legacy) | Loads a local IndexedDB entry by ID. Works only on the same browser profile where the history entry exists. | ?edit=42 |
Local Storage API
BlurShot uses IndexedDB for storing edit history. Extensions and integrations can access this data to build features like "recently edited" lists or batch processing workflows.
Reading Edit History
// Access BlurShot's IndexedDB
const db = await window.indexedDB.open('BlurShotHistory', 1);
// Get all edit history
const transaction = db.transaction(['edits'], 'readonly');
const store = transaction.objectStore('edits');
const allEdits = await store.getAll();
// Each edit contains:
// {
// id: number,
// timestamp: number,
// imageDataUrl: string,
// imageName: string,
// editCount: number,
// thumbnail: string
// }Browser Extension Example
Chrome Extension Manifest
{
"manifest_version": 3,
"name": "Screenshot Editor Extension",
"version": "1.0",
"permissions": ["activeTab", "storage"],
"action": {
"default_popup": "popup.html"
},
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
}
}Extension Background Script
// Capture screenshot and send to BlurShot
chrome.action.onClicked.addListener(async (tab) => {
// Capture visible tab
const screenshot = await chrome.tabs.captureVisibleTab(
null,
{ format: 'png' }
);
// Strip the data URI prefix to get just the base64 data
const base64Data = screenshot.split(',')[1];
// Create share payload
const shareState = {
i: base64Data,
n: 'screenshot.png',
c: 1
};
// Encode and create share URL
const encoded = btoa(JSON.stringify(shareState));
const blurShotUrl = `https://blurshot.io/#s=${encodeURIComponent(encoded)}`;
chrome.tabs.create({ url: blurShotUrl });
});Framework Integration Examples
Examples showing how to integrate BlurShot's share link functionality into popular frontend frameworks.
React Component Example
import React, { useState } from 'react';
function ScreenshotEditor() {
const [imageData, setImageData] = useState(null);
const handleFileUpload = (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
setImageData(event.target.result);
};
reader.readAsDataURL(file);
};
const openInBlurShot = () => {
if (!imageData) return;
// Strip the data URI prefix to get base64
const base64Data = imageData.split(',')[1];
// Create share payload
const shareState = {
i: base64Data,
n: 'screenshot.png',
c: 0
};
// Encode and create URL
const encoded = btoa(JSON.stringify(shareState));
const blurShotUrl = `https://blurshot.io/#s=${encodeURIComponent(encoded)}`;
// Open in new window
window.open(blurShotUrl, '_blank');
};
return (
<div className="screenshot-editor">
<input
type="file"
accept="image/*"
onChange={handleFileUpload}
className="file-input"
/>
{imageData && (
<button
onClick={openInBlurShot}
className="btn-primary"
>
Edit in BlurShot
</button>
)}
</div>
);
}
export default ScreenshotEditor;Vue Component Example
<template>
<div class="screenshot-editor">
<input
type="file"
accept="image/*"
@change="handleFileUpload"
class="file-input"
/>
<button
v-if="imageData"
@click="openInBlurShot"
class="btn-primary"
>
Edit in BlurShot
</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const imageData = ref(null);
const handleFileUpload = (event) => {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (e) => {
imageData.value = e.target.result;
};
reader.readAsDataURL(file);
};
const openInBlurShot = () => {
if (!imageData.value) return;
// Strip the data URI prefix to get base64
const base64Data = imageData.value.split(',')[1];
// Create share payload
const shareState = {
i: base64Data,
n: 'screenshot.png',
c: 0
};
// Encode and create URL
const encoded = btoa(JSON.stringify(shareState));
const blurShotUrl = `https://blurshot.io/#s=${encodeURIComponent(encoded)}`;
// Open in new window
window.open(blurShotUrl, '_blank');
};
</script>
<style scoped>
.screenshot-editor {
display: flex;
flex-direction: column;
gap: 1rem;
}
.btn-primary {
padding: 0.5rem 1rem;
background-color: #4f46e5;
color: white;
border: none;
border-radius: 0.5rem;
cursor: pointer;
}
.btn-primary:hover {
background-color: #4338ca;
}
</style>Python Automation Example
For DevOps teams automating screenshot sanitization in CI/CD pipelines or batch processing workflows.
import base64
import json
import urllib.parse
import webbrowser
from pathlib import Path
def sanitize_screenshot_with_blurshot(image_path: str, auto_open: bool = True) -> str:
"""
Generate a BlurShot share URL for a local image file.
Args:
image_path: Path to the image file to sanitize
auto_open: Whether to automatically open the URL in browser
Returns:
The BlurShot share URL
Example:
>>> sanitize_screenshot_with_blurshot('screenshot.png')
'https://blurshot.io/#s=eyJpIjoiL...'
"""
# Read and encode the image file
image_file = Path(image_path)
if not image_file.exists():
raise FileNotFoundError(f"Image not found: {image_path}")
# Read image as base64
with open(image_file, 'rb') as f:
image_data = base64.b64encode(f.read()).decode('utf-8')
# Create share payload matching BlurShot's format
share_state = {
"i": image_data, # Image data (base64)
"n": image_file.name, # Filename
"c": 0 # Edit count (0 for new)
}
# Encode and create share URL
encoded = base64.b64encode(
json.dumps(share_state).encode('utf-8')
).decode('utf-8')
blurshot_url = f"https://blurshot.io/#s={urllib.parse.quote(encoded)}"
if auto_open:
webbrowser.open(blurshot_url)
return blurshot_url
# Batch processing example
def batch_sanitize_screenshots(directory: str, pattern: str = "*.png"):
"""
Process multiple screenshots in a directory.
Example:
>>> batch_sanitize_screenshots('./screenshots', '*.png')
"""
screenshots_dir = Path(directory)
results = []
for image_file in screenshots_dir.glob(pattern):
try:
url = sanitize_screenshot_with_blurshot(
str(image_file),
auto_open=False
)
results.append({
"file": image_file.name,
"url": url,
"status": "success"
})
print(f"✓ Generated URL for {image_file.name}")
except Exception as e:
results.append({
"file": image_file.name,
"error": str(e),
"status": "failed"
})
print(f"✗ Failed to process {image_file.name}: {e}")
# Save URLs to file for team distribution
with open('blurshot_urls.json', 'w') as f:
json.dump(results, f, indent=2)
return results
if __name__ == "__main__":
# Single screenshot example
url = sanitize_screenshot_with_blurshot('bug-report-screenshot.png')
print(f"BlurShot URL: {url}")
# Batch processing example
# batch_sanitize_screenshots('./support-tickets/', '*.png')Troubleshooting Integration Issues
Common issues when integrating BlurShot and their solutions.
❌ Share URL Too Long (413 Payload Too Large)
Problem: Large images (>5MB) create URLs exceeding browser/server limits.
✓ Solution:
- Resize images before encoding (recommended max: 1920x1080)
- Compress to JPEG with 80-90% quality instead of PNG
- BlurShot automatically compresses images >10MB on load
- URL length limit: ~200KB of base64-encoded data
// Example: Compress image before sharing
const canvas = document.createElement('canvas');
canvas.width = Math.min(1920, img.width);
canvas.height = Math.min(1080, img.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
const base64 = canvas.toDataURL('image/jpeg', 0.85).split(',')[1]; ❌ CORS Errors When Loading External Images
Problem: Browser blocks loading images from external domains due to CORS policy.
✓ Solution:
- Use
crossOrigin="anonymous"on img elements - Ensure your image server sends proper CORS headers
- Alternative: Fetch image via your backend proxy to avoid CORS
- For local development: Use a CORS proxy or fetch via your server
// Correct way to load cross-origin images
const img = new Image();
img.crossOrigin = 'anonymous';
img.src = 'https://example.com/screenshot.png'; ❌ Invalid Base64 Encoding Errors
Problem: Share URLs fail to load with "Invalid encoding" or "Failed to parse state" errors.
✓ Solution:
- Strip the
data:image/png;base64,prefix from data URLs - Use
encodeURIComponent()on the final URL parameter - Ensure JSON is valid before encoding (use JSON.stringify)
- Test with small images first to isolate encoding issues
// ❌ Wrong - includes data URI prefix
const wrong = btoa(JSON.stringify({ i: dataUrl }));
// ✓ Correct - strip prefix first
const base64Only = dataUrl.split(',')[1];
const correct = btoa(JSON.stringify({ i: base64Only, n: 'file.png', c: 0 })); ❌ Feature Not Working in Older Browsers
Problem: BlurShot requires modern browser APIs not available in IE11 or older browsers.
✓ Solution:
- Minimum requirements: Chrome 90+, Firefox 88+, Safari 14+, Edge 90+
- BlurShot uses Canvas API, FileReader API, and modern JavaScript (ES2020+)
- For older browser support: Provide a fallback download link or server-side processing
- Test using
typeof FileReader !== 'undefined'
❌ Slow Performance with Large Images
Problem: BlurShot becomes slow or unresponsive when processing very large images (>10MB or >4K resolution).
✓ Solution:
- BlurShot automatically downsamples images >4096x4096px
- For batch processing: Process one image at a time (don't open multiple tabs)
- Recommended max: 25MB file size, 4K resolution
- Use Web Workers for preprocessing if integrating into your app
Privacy Considerations
⚠️ Important Privacy Note
When integrating BlurShot, ensure you respect user privacy:
- Never send images to external servers without explicit user consent
- Keep all processing client-side whenever possible
- If using iframe integration, use sandbox attributes appropriately
- Respect users' edit history privacy - don't sync or upload without permission
Support & Questions
Have questions about integrating BlurShot into your application or browser extension?
Contact SupportRelated Resources
Looking for more privacy tools?TextToolboxoffers complementary features for your workflow.
