Pinboard API Habits for Developers: Why POST /v1/posts/add Still Matters
Cowpin
10/11/2026

Pinboard never won a design award for REST purity. GET /v1/posts/add with
twenty query parameters is weird — and still copied because a generation of
scripts, iOS shortcuts, and desktop clients hard-coded that shape.
Cowpin implements the same Pinboard v1 subset under
https://www.cowpin.com/api/v1/ so you can point old tooling at a host that
still answers — then optionally grow into modern POST endpoints and MCP when
you need more than “fire-and-forget URL append.”
This post is the habits that keep automation reliable, not a full endpoint catalog (see automate your bookmarks for cron examples).
Habit 1: Treat posts/add as idempotent append
Pinboard (and Cowpin) replace=no (default) skips duplicate URLs; replace=yes
updates title, tags, and notes on collision. Scripts that blindly replace=yes
every run will overwrite human edits in the web app.
Developer habit: default replace=no for ingest pipelines; use replace=yes
only when your job owns the row (CI doc links, bot-generated tags).
curl -sG "https://www.cowpin.com/api/v1/posts/add" \
--data-urlencode "auth_token=$USER:$TOKEN" \
--data-urlencode "format=json" \
--data-urlencode "url=$URL" \
--data-urlencode "description=$TITLE" \
--data-urlencode "tags=ingest bot" \
--data-urlencode "replace=no"
Habit 2: Always pass format=json in new code
Pinboard defaults to XML. Cowpin matches that quirk. New scripts should set
format=json on every call so you are not parsing XML in 2026 because a blog
post from 2010 omitted the flag.
Habit 3: Poll posts/update before posts/all
posts/all is the hammer — and on Cowpin, like Pinboard, it is rate-limited
(roughly one full-library pull per token every five minutes, plus read buckets).
The cheap habit:
GET /api/v1/posts/update— last change timestamp- If unchanged, skip the heavy pull
- If changed,
posts/recentfor deltas orposts/allwhen you truly need everything
Nightly backups belong on posts/all with backoff; real-time hooks belong
on posts/add from your pipeline, not polling the whole library every minute.
Habit 4: Map fields the Pinboard way
Confusion that breaks imports:
| Pinboard param | Cowpin field |
|---|---|
description | Title |
extended | Longer note / description |
tags | Space-separated tag string |
toread=yes | Read-later queue |
shared=yes | Public (default is private) |
Getting description vs extended backwards produces libraries full of empty
titles — the sort of bug that only shows up in Netscape export round-trips.
Habit 5: Username in auth_token is not decorative
Token format is yourCowpinUsername:cowpin_…. The username must match the
account (set in settings). Scripts that paste token:token fail auth in ways
that look like “API down.”
Create tokens under Settings → API — shown once at creation, same mental model as Pinboard’s token screen.
When GET posts/add is not enough
Large bodies or JSON pipelines — use POST /api/bookmark/quick-add with
Authorization: Bearer cowpin_… (extension and iOS Shortcut
use this path). Same account, fewer query-string length limits.
Agents that search, not just append — MCP tools and agent wallet
flows sit above raw Pinboard compat: save and recall archived text without
you wiring posts/get yourself.
Bulk import — one-time Pinboard JSON/HTML export belongs in Settings → Import,
not ten thousand sequential posts/add calls hammering write rate limits.
Why this matters for product choice
Apps that killed Pinboard compat usually killed your scripts at the same time. When a bookmark service shuts down, the export is often fine; the daily cron is what breaks.
Cowpin’s north star is an export-first library: Pinboard-shaped API for legacy
habits, standard exports for migration, MCP for the next decade. If your job is
“POST interesting URLs from the shell,” /api/v1/posts/add still matters —
even if the method name says GET.
Point your next script at Cowpin
- Create an API token in Settings
- Swap
api.pinboard.in→www.cowpin.com/api - Add
format=json - Respect
posts/update+posts/allrate limits