Skip to content

Exports

Export endpoints manage encrypted bookmark backups stored in R2.


GET /api/v1/exports

Returns all exports for the authenticated user.

Required (Cookie)

Terminal window
curl https://app.bitmarks.sh/api/v1/exports \
-b "bitmarks_session=YOUR_TOKEN"

Status: 200 OK

{
"exports": [
{
"id": "exp_abc123",
"created_at": 1735430400000,
"status": "completed",
"size_bytes": 1048576,
"bookmark_count": 500,
"expires_at": 1736035200000
}
]
}
FieldTypeDescription
idstringUnique export identifier
created_atintegerCreation timestamp (Unix ms)
statusstringpending, processing, completed, failed
size_bytesintegerSize of export file in bytes
bookmark_countintegerNumber of bookmarks in export
expires_atintegerWhen download link expires (Unix ms)

POST /api/v1/exports

Creates a new encrypted export of all bookmarks.

Required (Cookie)

Terminal window
curl -X POST https://app.bitmarks.sh/api/v1/exports \
-b "bitmarks_session=YOUR_TOKEN"

Status: 201 Created

{
"id": "exp_new123",
"created_at": 1735430400000,
"status": "processing",
"size_bytes": null,
"bookmark_count": null,
"expires_at": null
}
  1. pending - Export request received
  2. processing - Collecting and encrypting bookmarks
  3. completed - Ready for download
  4. failed - Error during processing

GET /api/v1/exports/:id

Downloads a specific export file.

Required (Cookie)

ParamTypeDescription
idstringExport ID
Terminal window
curl https://app.bitmarks.sh/api/v1/exports/exp_abc123 \
-b "bitmarks_session=YOUR_TOKEN" \
-o backup.dat

Status: 200 OK

Content-Type: application/octet-stream
Content-Disposition: attachment; filename="bitmarks-export-2025-01-15.dat"

Binary encrypted data.

Status: 404 Not Found

{
"error": "Export not found"
}

Status: 400 Bad Request (if export not completed)

{
"error": "Export not ready for download"
}

DELETE /api/v1/exports/:id

Deletes an export from R2 storage.

Required (Cookie)

ParamTypeDescription
idstringExport ID
Terminal window
curl -X DELETE https://app.bitmarks.sh/api/v1/exports/exp_abc123 \
-b "bitmarks_session=YOUR_TOKEN"

Status: 200 OK

{
"success": true
}

Status: 404 Not Found

{
"error": "Export not found"
}

async function createBackup() {
// 1. Create export
const createResponse = await fetch('/api/v1/exports', {
method: 'POST',
credentials: 'include'
});
const { id } = await createResponse.json();
// 2. Poll for completion
let status = 'processing';
while (status === 'processing' || status === 'pending') {
await new Promise(resolve => setTimeout(resolve, 1000));
const listResponse = await fetch('/api/v1/exports', {
credentials: 'include'
});
const { exports } = await listResponse.json();
const exportInfo = exports.find(e => e.id === id);
status = exportInfo.status;
}
if (status === 'completed') {
// 3. Download
const downloadResponse = await fetch(`/api/v1/exports/${id}`, {
credentials: 'include'
});
return downloadResponse.blob();
} else {
throw new Error('Export failed');
}
}
async function restoreBackup(blob, encryptionKey) {
// 1. Read encrypted data
const arrayBuffer = await blob.arrayBuffer();
const encryptedData = new Uint8Array(arrayBuffer);
// 2. Decrypt
const { downloadEncrypted } = await import('@bitmarks.sh/core');
const decrypted = await decrypt(encryptedData, encryptionKey);
// 3. Parse bookmarks
const bookmarks = JSON.parse(new TextDecoder().decode(decrypted));
// 4. Import via sync/import endpoint
const importResponse = await fetch('/api/v1/sync/import', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ bookmarks })
});
return importResponse.json();
}

{
"version": "1.0",
"exported_at": "2025-01-15T12:00:00.000Z",
"bookmark_count": 500,
"bookmarks": [
{
"id": "bm_abc123",
"title": "Example",
"url": "https://example.com",
"source": "bookmarks",
"dateAdded": "2024-01-01T00:00:00.000Z",
"path": "Bookmarks/Work"
}
]
}
  • Algorithm: XChaCha20-Poly1305
  • Key: User’s encryption key
  • Storage: R2 with Server-Side Encryption with Customer-Provided Keys (SSEC)