> This skill provides comprehensive instructions for interacting with the Raindrop.io bookmarks service via its REST API using curl and jq. It covers authentication, CRUD operations for collections, raindrops (bookmarks), tags, highlights, filters, import/export, and backups. Use this skill whenever the user asks to work with their bookmarks from Raindrop.io, including reading, creating, updating, deleting, searching, or organising bookmarks and collections.
npx skills add https://github.com/intellectronica/agent-skills --skill raindrop-api
This skill enables interaction with the Raindrop.io bookmarks service through its REST API. Use curl and jq for direct REST calls.
Official API documentation: https://developer.raindrop.io/
Resolve the API token in this order:
RAINDROP_TOKENTo verify a token exists in the environment:
[ -n "$RAINDROP_TOKEN" ] && echo "Token available" || echo "Token not set"
Quick setup: For personal use or development, generate a test token at https://app.raindrop.io/settings/integrations — open your app and copy the "Test token". Test tokens do not expire.
For full OAuth2 flow details, see the Authentication section below.
All requests require the Authorization header with Bearer token:
curl -s -H "Authorization: Bearer $RAINDROP_TOKEN" \
"https://api.raindrop.io/rest/v1/ENDPOINT"
For POST/PUT requests with JSON body:
curl -s -X POST \
-H "Authorization: Bearer $RAINDROP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"key": "value"}' \
"https://api.raindrop.io/rest/v1/ENDPOINT"
Test the token by retrieving the current user:
curl -s -H "Authorization: Bearer $RAINDROP_TOKEN" \
"https://api.raindrop.io/rest/v1/user" | jq '.user.fullName'
https://api.raindrop.io/rest/v1/Before executing any destructive action (DELETE, bulk update, move to trash), always ask the user for confirmation using AskUserQuestion. A single confirmation suffices for a logical group of related actions.
Destructive actions include:
Read-only operations (GET requests) do not require confirmation.
Docs: https://developer.raindrop.io/v1/raindrops
| Operation | Method | Endpoint |
|-----------|--------|----------|
| Get raindrop | GET | /raindrop/{id} |
| Create raindrop | POST | /raindrop |
| Update raindrop | PUT | /raindrop/{id} |
| Remove raindrop | DELETE | /raindrop/{id} |
| Upload file | PUT | /raindrop/file |
| Upload cover | PUT | /raindrop/{id}/cover |
| Get permanent copy | GET | /raindrop/{id}/cache |
| Suggest (new URL) | POST | /raindrop/suggest |
| Suggest (existing) | GET | /raindrop/{id}/suggest |
Docs: https://developer.raindrop.io/v1/raindrops/single
Raindrop creation/update fields:
link (string, required for creation) - Bookmark URLtitle (string) - Bookmark titleexcerpt (string) - Short descriptionnote (string) - User notes (supports Markdown)tags (array of strings) - Tag namescollection (object) - {"$id": collectionId}type (string) - link, article, image, video, document, audioimportant (boolean) - Mark as favouriteorder (number) - Sort order (ascending)media (array) - Media/thumbnail infohighlights (array) - Text highlightscover (string) - Cover image URL, or <screenshot> for auto-capturepleaseParse (object) - {} to trigger background metadata parsingcreated (string) - ISO 8601 creation datelastUpdate (string) - ISO 8601 last update datereminder (object) - Reminder settingsDeletion behaviour: Removing a raindrop moves it to Trash (collection ID -99). Removing from Trash deletes permanently.
| Operation | Method | Endpoint |
|-----------|--------|----------|
| Get raindrops | GET | /raindrops/{collectionId} |
| Create multiple | POST | /raindrops |
| Update multiple | PUT | /raindrops/{collectionId} |
| Remove multiple | DELETE | /raindrops/{collectionId} |
| Export | GET | /raindrops/{collectionId}/export.{format} |
Docs: https://developer.raindrop.io/v1/raindrops/multiple
collectionId values:
0 - All raindrops-1 - Unsorted-99 - TrashQuery parameters for GET /raindrops/{collectionId}:
sort - Sort order: -created (default), created, score, -sort, title, -title, domain, -domainperpage - Results per page (max 50)page - Page number (0-indexed)search - Search query (see references/search-operators.md)nested - Boolean, include child collection bookmarksBulk update fields (PUT with ids array or search query):
important (boolean)tags (array) - Appends tags; empty array clears allmedia (array) - Appends; empty array clearscover (string) - URL or <screenshot>collection (object) - {"$id": collectionId} to moveExport formats: csv, html, zip
Docs: https://developer.raindrop.io/v1/collections
| Operation | Method | Endpoint |
|-----------|--------|----------|
| List root collections | GET | /collections |
| List child collections | GET | /collections/childrens |
| Get collection | GET | /collection/{id} |
| Create collection | POST | /collection |
| Update collection | PUT | /collection/{id} |
| Upload cover | PUT | /collection/{id}/cover |
| Delete collection | DELETE | /collection/{id} |
| Delete multiple | DELETE | /collections |
| Reorder/expand all | PUT | /collections |
| Merge collections | PUT | /collections/merge |
| Remove empty | PUT | /collections/clean |
| Empty trash | DELETE | /collection/-99 |
| System collection counts | GET | /user/stats |
| Search covers/icons | GET | /collections/covers/{text} |
| Featured covers | GET | /collections/covers |
Docs: https://developer.raindrop.io/v1/collections/methods
Collection fields:
title (string) - Collection nameview (string) - Display style: list, simple, grid, masonrypublic (boolean) - Public accessibilityparent (object) - {"$id": parentCollectionId} for nestingsort (number) - Sort positioncover (array) - Cover image URLsexpanded (boolean) - Whether subcollections are expandedcolor (string) - Collection colourSystem collections (non-removable):
-1 - "Unsorted"-99 - "Trash"Access levels (access.level):
1 - Read only2 - Collaborator (write)3 - Collaborator (write + manage)4 - OwnerFor sharing/collaborators, see references/collections-sharing.md.
Docs: https://developer.raindrop.io/v1/tags
| Operation | Method | Endpoint |
|-----------|--------|----------|
| Get tags | GET | /tags/{collectionId} |
| Rename tag | PUT | /tags/{collectionId} |
| Merge tags | PUT | /tags/{collectionId} |
| Remove tag(s) | DELETE | /tags/{collectionId} |
collectionId: Omit or use 0 for tags from all collections.
Get tags response:
{
"result": true,
"items": [{"_id": "tagname", "count": 42}]
}
Rename tag:
curl -s -X PUT \
-H "Authorization: Bearer $RAINDROP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"replace": "new-name", "tags": ["old-name"]}' \
"https://api.raindrop.io/rest/v1/tags/0"
Merge tags (same endpoint, multiple tags in array):
curl -s -X PUT \
-H "Authorization: Bearer $RAINDROP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"replace": "merged-name", "tags": ["tag1", "tag2", "tag3"]}' \
"https://api.raindrop.io/rest/v1/tags/0"
Remove tags:
curl -s -X DELETE \
-H "Authorization: Bearer $RAINDROP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"tags": ["tag-to-remove"]}' \
"https://api.raindrop.io/rest/v1/tags/0"
Docs: https://developer.raindrop.io/v1/highlights
| Operation | Method | Endpoint |
|-----------|--------|----------|
| Get all highlights | GET | /highlights |
| Get collection highlights | GET | /highlights/{collectionId} |
| Get raindrop highlights | GET | /raindrop/{id} |
| Add highlight | PUT | /raindrop/{id} |
| Update highlight | PUT | /raindrop/{id} |
| Delete highlight | PUT | /raindrop/{id} |
For details, see references/highlights.md.
Docs: https://developer.raindrop.io/v1/filters
curl -s -H "Authorization: Bearer $RAINDROP_TOKEN" \
"https://api.raindrop.io/rest/v1/filters/{collectionId}" | jq '.'
Use 0 for all collections. Returns aggregated counts for broken links, duplicates, favourites, untagged items, tags, and content types.
Query parameters:
tagsSort - -count (default) or _id (alphabetical)search - Additional search filterDocs: https://developer.raindrop.io/v1/user
| Operation | Method | Endpoint |
|-----------|--------|----------|
| Get current user | GET | /user |
| Update user | PUT | /user |
Docs: https://developer.raindrop.io/v1/import
| Operation | Method | Endpoint |
|-----------|--------|----------|
| Parse URL | GET | /import/url/parse?url={url} |
| Check URL existence | POST | /import/url/exists |
| Parse HTML bookmark file | POST | /import/file |
Docs: https://developer.raindrop.io/v1/backups
| Operation | Method | Endpoint |
|-----------|--------|----------|
| List backups | GET | /backups |
| Download backup | GET | /backup/{id}.{format} |
| Generate new backup | GET | /backup |
Formats: html or csv
Docs: https://developer.raindrop.io/v1/authentication/token
For apps accessing other users' data (not personal use), use the full OAuth2 flow:
Direct users to:
https://raindrop.io/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code
curl -s -X POST "https://raindrop.io/oauth/access_token" \
-H "Content-Type: application/json" \
-d '{
"code": "AUTH_CODE",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"redirect_uri": "YOUR_REDIRECT_URI",
"grant_type": "authorization_code"
}' | jq '.'
Response:
{
"access_token": "...",
"refresh_token": "...",
"expires_in": 1209599,
"token_type": "Bearer"
}
Access tokens expire after two weeks. Refresh with:
curl -s -X POST "https://raindrop.io/oauth/access_token" \
-H "Content-Type: application/json" \
-d '{
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"refresh_token": "YOUR_REFRESH_TOKEN",
"grant_type": "refresh_token"
}' | jq '.'
Check HTTP status codes:
200 - Success204 - Success, no content400 - Bad request401 - Authentication failed (check token)403 - Forbidden (insufficient permissions)404 - Resource not found429 - Rate limited (120 req/min exceeded)5xx - Server errorresponse=$(curl -s -w "\n%{http_code}" \
-H "Authorization: Bearer $RAINDROP_TOKEN" \
"https://api.raindrop.io/rest/v1/raindrops/0")
http_code=$(echo "$response" | tail -1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
echo "$body" | jq '.'
else
echo "Error: HTTP $http_code"
echo "$body"
fi
curl -s -H "Authorization: Bearer $RAINDROP_TOKEN" \
"https://api.raindrop.io/rest/v1/raindrops/COLLECTION_ID?perpage=50" | jq '.items[] | {title, link}'
curl -s -X POST \
-H "Authorization: Bearer $RAINDROP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"link": "https://example.com",
"title": "Example Site",
"tags": ["reference", "example"],
"collection": {"$id": COLLECTION_ID},
"pleaseParse": {}
}' \
"https://api.raindrop.io/rest/v1/raindrop" | jq '.'
# Search across all collections
curl -s -H "Authorization: Bearer $RAINDROP_TOKEN" \
"https://api.raindrop.io/rest/v1/raindrops/0?search=YOUR_QUERY" | jq '.items[] | {title, link}'
# Search with tag filter
curl -s -H "Authorization: Bearer $RAINDROP_TOKEN" \
"https://api.raindrop.io/rest/v1/raindrops/0?search=%23tagname" | jq '.items[] | {title, link}'
See references/search-operators.md for the complete search query syntax.
curl -s -X PUT \
-H "Authorization: Bearer $RAINDROP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"collection": {"$id": TARGET_COLLECTION_ID}}' \
"https://api.raindrop.io/rest/v1/raindrop/RAINDROP_ID" | jq '.'
curl -s -H "Authorization: Bearer $RAINDROP_TOKEN" \
"https://api.raindrop.io/rest/v1/tags/0" | jq '.items[] | {tag: ._id, count}'
curl -s -X POST \
-H "Authorization: Bearer $RAINDROP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "My Collection", "view": "list"}' \
"https://api.raindrop.io/rest/v1/collection" | jq '.'
# Root collections
curl -s -H "Authorization: Bearer $RAINDROP_TOKEN" \
"https://api.raindrop.io/rest/v1/collections" | jq '.items[] | {id: ._id, title}'
# Child collections
curl -s -H "Authorization: Bearer $RAINDROP_TOKEN" \
"https://api.raindrop.io/rest/v1/collections/childrens" | jq '.items[] | {id: ._id, title, parent: .parent."$id"}'
page=0
while true; do
response=$(curl -s -H "Authorization: Bearer $RAINDROP_TOKEN" \
"https://api.raindrop.io/rest/v1/raindrops/0?perpage=50&page=$page")
count=$(echo "$response" | jq '.items | length')
echo "$response" | jq '.items[] | {title, link}'
if [ "$count" -lt 50 ]; then
break
fi
page=$((page + 1))
done
# Export all bookmarks as CSV
curl -s -H "Authorization: Bearer $RAINDROP_TOKEN" \
"https://api.raindrop.io/rest/v1/raindrops/0/export.csv" -o bookmarks.csv
# Export as HTML
curl -s -H "Authorization: Bearer $RAINDROP_TOKEN" \
"https://api.raindrop.io/rest/v1/raindrops/0/export.html" -o bookmarks.html
curl -s -X POST \
-H "Authorization: Bearer $RAINDROP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"urls": ["https://example.com"]}' \
"https://api.raindrop.io/rest/v1/import/url/exists" | jq '.'
Raindrop uses page-based pagination (not cursor-based):
page - Page number (0-indexed)perpage - Items per page (max 50, default 25 for highlights)When the number of items returned is less than perpage, you have reached the last page.
Collections are organised hierarchically. Reconstructing the full sidebar requires:
GET /user - Returns groups array with collection orderingGET /collections - Root collectionsGET /collections/childrens - Nested collectionsRoot collection sort order is persisted in the user's groups[].collections array. Child collection sort order is stored in the collection's sort field.
For detailed documentation on specific topics, consult:
references/search-operators.md - Search query syntax and operatorsreferences/collections-sharing.md - Collection sharing and collaboratorsreferences/highlights.md - Highlight managementGET /userYou are a dependency security expert specializing in vulnerability scanning, license compliance, and supply chain security. Analyze project dependencies for known vulnerabilities, licensing issues, outdated packages, and provide actionable remediation strategies.
Perform language and framework specific security best-practice reviews and suggest improvements. Trigger only when the user explicitly requests security best practices guidance, a security review/report, or secure-by-default coding help. Trigger only for supported languages (python, javascript/typescript, go). Do not trigger for general code review, debugging, or non-security tasks.
Implement authentication and authorization with Better Auth - a framework-agnostic TypeScript authentication framework. Features include email/password authentication with verification, OAuth providers (Google, GitHub, Discord, etc.), two-factor authentication (TOTP, SMS), passkeys/WebAuthn support, session management, role-based access control (RBAC), rate limiting, and database adapters. Use when adding authentication to applications, implementing OAuth flows, setting up 2FA/MFA, managing user sessions, configuring authorization rules, or building secure authentication systems for web applications.
Package entire code repositories into single AI-friendly files using Repomix. Capabilities include pack codebases with customizable include/exclude patterns, generate multiple output formats (XML, Markdown, plain text), preserve file structure and context, optimize for AI consumption with token counting, filter by file types and directories, add custom headers and summaries. Use when packaging codebases for AI analysis, creating repository snapshots for LLM context, analyzing third-party libraries, preparing for security audits, generating documentation context, or evaluating unfamiliar codebases.
You are a dependency security expert specializing in vulnerability scanning, license compliance, and supply chain security. Analyze project dependencies for known vulnerabilities, licensing issues, outdated packages, and provide actionable remediation strategies.
Expert patterns for HubSpot CRM integration including OAuth authentication, CRM objects, associations, batch operations, webhooks, and custom objects. Covers Node.js and Python SDKs.
Perform language and framework specific security best-practice reviews and suggest improvements. Use when the user explicitly requests security best practices guidance, a security review or report, or secure-by-default coding help. Supports Python, JavaScript/TypeScript, and Go. Do NOT use for general code review, debugging, threat modeling (use security-threat-model), or non-security tasks.
Configures API gateways for routing, authentication, rate limiting, and request transformation in microservice architectures. Use when setting up Kong, Nginx, AWS API Gateway, or Traefik for centralized API management.
Take intellectronica/raindrop-api from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.