- Export Pocket archive. Extract them into a directory. There will be a few csv file
- Install LINKDING
- Get the LINKDING API and TOKEN. Update script
- Get ruby dependencies with
gem install httpx
- Run
ruby import.rb
Created
June 8, 2025 21:50
-
-
Save v9n/7453a635acc2d7a84a28619b1e2ede7c to your computer and use it in GitHub Desktop.
Simple script to import Pocket Export to Linkding
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
require 'csv' | |
require 'json' | |
require 'httpx' # Require the httpx gem | |
# --- Configuration --- | |
LINKDING_API_URL = '<linkding url>/api/bookmarks/' # Replace with your Linkding API URL | |
LINKDING_API_TOKEN = '<linkding api token>' # Replace with your Linkding API token | |
POCKET_CSV_DIR = File.expand_path('<path-to-pocket-scv-directory>') # Replace with the path to your Pocket CSV directory | |
# --- Script Logic --- | |
def import_bookmark(bookmark_data) | |
headers = { | |
'Content-Type' => 'application/json', | |
'Authorization' => "Token #{LINKDING_API_TOKEN}" | |
} | |
begin | |
response = HTTPX.post(LINKDING_API_URL, json: bookmark_data, headers: headers) | |
if response.status == 201 | |
puts "Successfully imported: #{bookmark_data[:url]}" | |
else | |
puts "Failed to import #{bookmark_data[:url]}. Status: #{response.status}, Body: #{response.body.to_s}" | |
end | |
rescue HTTPX::Error => e | |
puts "HTTPX error importing #{bookmark_data[:url]}: #{e.message}" | |
rescue StandardError => e | |
puts "General error importing #{bookmark_data[:url]}: #{e.message}" | |
end | |
end | |
def process_pocket_csv(csv_file_path) | |
puts "Processing CSV file: #{csv_file_path}" | |
CSV.foreach(csv_file_path, headers: true) do |row| | |
title = row['title'] | |
url = row['url'] | |
time_added = row['time_added'] # This is a Unix timestamp | |
tags = row['tags'] | |
status = row['status'] | |
# Skip invalid rows (e.g., if URL is missing or malformed) | |
next if url.nil? || url.strip.empty? || !url.start_with?('http') | |
# Prepare data for Linkding API | |
bookmark = { | |
url: url, | |
title: title.to_s.empty? ? url : title, # Use URL as title if title is empty | |
description: '', # Pocket export doesn't seem to have descriptions, so leave empty | |
is_archived: status == 'read', # Linkding uses 'is_archived' for read status | |
tag_names: tags.to_s.split(',').map(&:strip).reject(&:empty?) # Split tags by comma and clean up | |
} | |
# Convert Unix timestamp to ISO 8601 format for Linkding's `added_at` | |
begin | |
bookmark[:added_at] = Time.at(time_added.to_i).iso8601 | |
rescue ArgumentError => e | |
puts "Warning: Could not parse timestamp '#{time_added}' for URL: #{url}. Skipping 'added_at'. Error: #{e.message}" | |
bookmark.delete(:added_at) # Remove if invalid | |
end | |
import_bookmark(bookmark) | |
sleep(0.1) # Be kind to the API, add a small delay | |
end | |
end | |
# --- Main Execution --- | |
puts "Starting Pocket to Linkding import..." | |
Dir.glob(File.join(POCKET_CSV_DIR, 'part_*.csv')).sort.each do |csv_file| | |
process_pocket_csv(csv_file) | |
end | |
puts "Import process finished." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment