Skip to content

Instantly share code, notes, and snippets.

@andrewgross
Created February 12, 2025 21:04
Show Gist options
  • Save andrewgross/09f08c4e3679950efe05f2ccdb607531 to your computer and use it in GitHub Desktop.
Save andrewgross/09f08c4e3679950efe05f2ccdb607531 to your computer and use it in GitHub Desktop.
Remarkable API Upload
import base64
import json
import requests
from pathlib import Path
from typing import Union, Optional
class RemarkableError(Exception):
pass
def upload_to_remarkable(
file_path: Union[str, Path, bytes],
filename: str,
device_token: str,
user_token: str,
content_type: str = "application/pdf"
) -> dict:
"""
Upload a document to Remarkable Cloud.
Args:
file_path: Path to file or bytes content
filename: Name to use for the document on Remarkable
device_token: Remarkable device token
user_token: Remarkable user token
content_type: Content type (default: PDF)
Returns:
dict: Response from the upload API
Raises:
RemarkableError: If upload fails
"""
# Validate tokens
if not (device_token and user_token):
raise RemarkableError("Both device_token and user_token are required")
if not (device_token.startswith('ey') and user_token.startswith('ey')):
raise RemarkableError("Invalid token format")
# Extract tectonic from user token
try:
token_parts = user_token.split('.')
payload = json.loads(base64.b64decode(token_parts[1] + "=" * (-len(token_parts[1]) % 4)))
tectonic = payload.get('tectonic')
except Exception as e:
raise RemarkableError(f"Failed to parse user token: {str(e)}")
# Determine base URL
base_url = (f"https://{tectonic}.tectonic.remarkable.com"
if tectonic
else "https://internal.cloud.remarkable.com")
# Prepare file data
if isinstance(file_path, (str, Path)):
with open(file_path, 'rb') as f:
file_data = f.read()
else:
file_data = file_path
# Prepare upload request
upload_url = f"{base_url}/doc/v2/files"
headers = {
'Authorization': f'Bearer {user_token}',
'Content-Type': content_type,
'rM-Meta': base64.b64encode(
json.dumps({"file_name": filename}).encode()
).decode()
}
# Upload with retry
for _ in range(2): # Retry once on auth error
try:
response = requests.post(
upload_url,
headers=headers,
data=file_data
)
if response.status_code == 401: # Unauthorized
# In a real implementation, you'd refresh the token here
continue
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
if _ == 1: # Last retry
raise RemarkableError(f"Upload failed: {str(e)}")
raise RemarkableError("Upload failed after retries")
# Example usage:
if __name__ == "__main__":
try:
result = upload_to_remarkable(
"document.pdf",
"My Document",
device_token="eyJ...", # Your device token
user_token="eyJ..." # Your user token
)
print(f"Upload successful: {result}")
except RemarkableError as e:
print(f"Upload failed: {str(e)}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment