Created
July 15, 2025 15:40
-
-
Save dctanner/e0e00b32d4df6936a8a19ac7da89a056 to your computer and use it in GitHub Desktop.
Import your Pocket saves into Pinboard
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Pocket to Pinboard Import Script | |
This script imports bookmarks from a Pocket export CSV file to Pinboard. | |
To run this script with uv in a single command: | |
uv run --with pinboard https://gist.githubusercontent.com/dctanner/e0e00b32d4df6936a8a19ac7da89a056/raw/pocket_to_pinboard_standalone.py pocket_export.csv username:api_token | |
Or download first and run: | |
curl -O https://gist.githubusercontent.com/dctanner/e0e00b32d4df6936a8a19ac7da89a056/raw/pocket_to_pinboard_standalone.py | |
uv run --with pinboard pocket_to_pinboard_standalone.py pocket_export.csv username:api_token | |
Requirements: | |
- uv (install from https://github.com/astral-sh/uv) | |
- A Pocket export CSV file (export from https://getpocket.com/export) | |
- A Pinboard API token (get from https://pinboard.in/settings/password) | |
Usage: | |
python pocket_to_pinboard_standalone.py <pocket_csv_file> <pinboard_api_token> | |
Arguments: | |
pocket_csv_file Path to the Pocket export CSV file | |
pinboard_api_token Pinboard API token in username:token format | |
Example: | |
python pocket_to_pinboard_standalone.py pocket_export.csv myusername:1234567890ABCDEF | |
Note: The script respects Pinboard's rate limiting (3 seconds between requests). | |
""" | |
import csv | |
import argparse | |
import time | |
from datetime import datetime | |
import pinboard | |
def parse_pocket_csv(csv_file): | |
"""Parse Pocket export CSV and return list of bookmarks.""" | |
bookmarks = [] | |
with open(csv_file, 'r', encoding='utf-8') as f: | |
reader = csv.DictReader(f) | |
for row in reader: | |
# Skip items without URLs | |
if not row.get('url'): | |
continue | |
bookmark = { | |
'url': row['url'], | |
'description': row.get('title', ''), | |
'tags': row.get('tags', '').split(',') if row.get('tags') else [], | |
'time': datetime.fromtimestamp(int(row.get('time_added', 0))) if row.get('time_added') else None, | |
'toread': row.get('status') == 'unread' | |
} | |
bookmarks.append(bookmark) | |
return bookmarks | |
def import_to_pinboard(bookmarks, api_token): | |
"""Import bookmarks to Pinboard.""" | |
pb = pinboard.Pinboard(api_token) | |
total = len(bookmarks) | |
successful = 0 | |
failed = 0 | |
print(f"Starting import of {total} bookmarks...") | |
for i, bookmark in enumerate(bookmarks, 1): | |
try: | |
# Pinboard API has rate limiting (1 request per 3 seconds) | |
if i > 1: | |
time.sleep(3) | |
pb.posts.add( | |
url=bookmark['url'], | |
description=bookmark['description'], | |
tags=' '.join(bookmark['tags']), | |
dt=bookmark['time'], | |
toread=bookmark['toread'] | |
) | |
successful += 1 | |
print(f"[{i}/{total}] Added: {bookmark['description'][:50]}...") | |
except Exception as e: | |
failed += 1 | |
print(f"[{i}/{total}] Failed to add {bookmark['url']}: {str(e)}") | |
print(f"\nImport complete!") | |
print(f"Successful: {successful}") | |
print(f"Failed: {failed}") | |
def main(): | |
parser = argparse.ArgumentParser(description='Import Pocket bookmarks to Pinboard') | |
parser.add_argument('csv_file', help='Path to Pocket export CSV file') | |
parser.add_argument('api_token', help='Pinboard API token (username:token format)') | |
args = parser.parse_args() | |
# Parse CSV | |
try: | |
bookmarks = parse_pocket_csv(args.csv_file) | |
print(f"Parsed {len(bookmarks)} bookmarks from CSV") | |
except Exception as e: | |
print(f"Error reading CSV file: {e}") | |
return 1 | |
# Import to Pinboard | |
try: | |
import_to_pinboard(bookmarks, args.api_token) | |
except Exception as e: | |
print(f"Error during import: {e}") | |
return 1 | |
return 0 | |
if __name__ == '__main__': | |
exit(main()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment