Pinboard API Documentation (v2)
Access
The API endpoint is https://api.pinboard.in/v2/.
Authentication
All requests require a valid username and API token. Users can find this information on their settings/password page.
If you are an app developer or run an online service, is important that you not store Pinboard passwords.
There are two ways to pass the API token in an API call. You can include it as a query parameter:
https://api.pinboard.in/v1/method?auth_token=user:123
Or you can put it in an HTTP header:
X-Auth-Token: user:123
If both alternatives are present, Pinboard will throw a too much authentication error.
Encoding
All entities are encoded as UTF-8. When length limits are given, they refer to logical characters rather than bytes.
All arguments should be passed URL-encoded.
Where multiple arguments are allowed, they should be separated by URL-encoded whitespace (apple+pear+orange)
HTTP Verbs
Pinboard tries to be REST-ful without making it a burden.
Most resources have their own endpoint. You request stuff with GET, modify stuff with PUT or POST, and delete with DELETE.
Since I find it hard to keep the difference between PUT and POST straight in my mind (one is idempotent, the other not), they are synonyms in the eyes of Pinboard server.
Pinboard does not handle PATCH. Use POST for partial updates.
Formats
All requests are returned as JSON, with MIME type application/json.
All responses have a top-level 'status' property in the JSON response. This is set to 'ok' if the request succeeded, and 'error' if there are errors. See the section on error reporting for how to check it.
{ status : "ok|error|server_error",
key1 : "....",
key2 : "...",
}
HTTP Status Codes
The API uses HTTP error codes where appropriate. Clients should be ready to encounter the following:
- 200 OK
- 201 Created
- 304 Not Modified
- 400 Bad Request
- 401 Unauthorized
- 402 Payment Required
- 403 Forbidden
- 404 Not Found
- 405 Method Not Allowed
- 429 Rate Limited (see section on rate limits)
- 500 Server Error
- 503 Service Unavailable
See the errors section below, and the method reference for details.
Error Reporting
Errors are marked in two ways. The server returns an appropriate HTTP error code. If no other code makes sense, the default is 400 Bad Request.
You'll also find an error code and message in the body of the JSON response.
Every response has a top-level field called status. A response with no errors will have its status field set to 'ok':
{ 'status' : 'ok',
'foo' { 'bar' ....} }
A response with an error will have the status field set to 'error', an error_code matching what is in the HTTP headers, and a human-readable error_message.
{ 'status' : 'error',
'error' : 'bad_url',
'error_code' : '400',
'error_message' : "URL did not have allowed scheme"
.... }
You should check for the following errors on every API call:
| 401 | no auth token | You failed to send an auth token. |
| 401 | unauthorized | The auth token you provided is not valid. |
| 402 | deadbeat | The account is locked in read-only mode for non-payment. |
| 402 | payment required | This is a premium feature (see archiving). |
| 429 | rate limited | You've exceeded the rate limits. |
| 503 | server error | Something is horked internally. |
| 599 | hit by bus | The Pinboard founder has died, taking all your data with him. |
Additional errors are documented with their individual API calls.
Data Types
The Pinboard API recognizes the following data types:
tag |
UTF-8 string up to 255 characters long. May not contain commas or whitespace. Please be aware that tags beginning with a period are treated as private and trigger special private tag semantics. |
url |
URL as defined by RFC 3986. Allowed schemes are http, https, javascript, mailto, ftp, gemini, gopher and file. |
string |
A UTF-8 string. |
boolean |
The value 0 or 1 |
integer |
An integer in the range [0,65535] |
id |
A hexadecimal string. Not case sensitive. |
datetime |
Date and time in ISO 8601 format (2010-12-11T19:48:02Z). |
User Rate Limits
Pinboard enforces account-based rate limits.
These limits apply to the user account, so if a user is using multiple API clients, their cumulative activity will count against that user's limit.
The general rate limit is 400 API requests every fifteen minutes. Some API calls have stricter rate limits, these are documented with the individual calls.
Rate limits will be adjusted based on API use patterns, with the goal of making them generous.
Rate-limited API calls return three special headers:
X-Requests-Remaining: 123
X-Reset-Time: 2020-02-12T12:12:04Z
X-Reset-Timestamp: 177384283
The X-Reset-Time header tells you when the request counter will reset. The X-Reset-Timestamp gives you the same value as a Unix timestamp, to spare you some date parsing.
Rate limits can change dynamically, so always check these headers.
If you hit the rate limit, you'll start to see 429 Too Many Requests server errors, and should back off appropriately. If possible, keep doubling the interval between requests until you stop receiving errors.
The purpose of user rate limits is to keep the API as fast and useful as possible for everyone. If the limits are seriously interfering with your use of the site, or you need special exemptions, talk to me!
Application Rate Limits
Requests to certain parts of the site are subject to an additional, per-application rate limit of 1000 requests per day:
/url/*
/site/recent/
/site/popular/
/site/search/*
/site/tag/*
/url/
These limits are imposed per app identifier or IP address.
The limit is cumulative (so making 400 requests to /url/ + 500 requests to /site/popular/ counts the same as making 900 requests to /url/).
Remember that requests using the If-Modified-Since and If-None-Match conditional request headers won't count against the rate limit unless they return fresh data.
If you have a use case that makes these rate limits onerous, please talk to me about it.
URL Normalization
All URLs submitted are normalized by default. This means:
- Shortened URLs get de-shortened
- Case-insensitive portions of the URL are converted to lower case
utc_*and similar tracking barnacles are stripped from the query string.
If you do not want incoming URLs normalized, set the exact_url flag to true.
Privacy Semantics
There are three privacy-related behaviors that sometimes catch people by surprise:
1. Users who have the PRIVACY LOCK setting enabled cannot save public bookmarks, or make their current bookmarks public. Attempts to do this will not raise an error, but the bookmark will be private.
2. Any tag beginning with a dot (.example) is treated as a private tag within Pinboard. Private tags are only visible to the user who created them.
3. Unread bookmarks are always treated as private, no matter whether they are marked private or public. Once the bookmark is marked 'read', the private or public flag will determine whether it's visible.
Things I Ask of You
Your app or service shouldn't pretend to be Pinboard, or allow users to form the impression that it's Pinboard. That includes the logo and app icon.
You shouldn't use the API to build up a data collection on your own service without my prior written consent. This holds especially for sitewide data like search results, global tags, and URL metadata.
If in doubt, get in touch with me and let me know what you're doing. I'm happy to grant permission for all kinds of projects as long as they protect user privacy.
You must not use any data you get from this API for ad targeting or marketing.
You must not share user data you get from the API with outside parties. This stuff is between me, you, and our users.
Don't mislead the API server about who you are (by using a fake User-Agent string, for example). If you develop app, please register and use an app identifier, and update it with each majore release.
If you want to use the API to assemble data for a research project, talk to me. Chances are there's a more direct way to get you there.
If you distribute a library that interacts with the API, please make sure it checks certificate validity with each API connection, even though that is annoying, and make sure it respects rate limits.
Support
To report bugs in the API or this documentation, please contact me at support@pinboard.in.
If you have an API feature idea, or complaint, please post it to the pinboard-dev Google group for discussion.
You can find me on Twitter as @pinboard and on IRC as #pinboard on freenode.