Build with Pinport
API reference
Build with your bookmarks. Start new integrations on API v2.
Authentication
All API requests must include your API key in the X-Api-Key header. You can create
API keys in your settings.
X-Api-Key: your-api-key Keep your API key secure! Don't share it or expose it in client-side code.
Base URL
All API requests should be made to: https://pinport.app/api/v2
Requests are limited to 120 per minute per API key; above that the API answers 429 Too Many Requests.
Response Format
All responses are JSON. Single bookmarks are returned as objects, lists inside a data array, and errors carry a message.
Bookmark Object
{
"id": "abc123",
"url": "https://example.com",
"title": "Example Website",
"note": "Why I saved this",
"tags": ["example", "demo"],
"createdAt": "2026-06-15T12:30:45.000Z",
"updatedAt": "2026-06-15T12:30:45.000Z"
} note is your own text about why you saved the URL. It is never filled in
automatically. updatedAt changes when you edit the bookmark; tag renames do not touch
it.
Error Response Example
{
"message": "URL is required"
} Endpoints
List Bookmarks
GET /bookmarks
GET /bookmarks?tag=recipes&tag=vegan&limit=50
GET /bookmarks?cursor=MjAyNi... Returns your bookmarks, newest first, one page at a time.
Query Parameters
tagoptional, repeatable - Only bookmarks carrying every given taguntaggedoptional - Only bookmarks without tagslimitoptional - Page size, 1-1000 (default 100)cursoroptional - ThenextCursorof the previous page
Response
Status: 200 OK or 400 Bad Request
{
"data": [
{
"id": "abc123",
"url": "https://example.com",
"title": "Example Website",
"note": "",
"tags": ["example"],
"createdAt": "2026-06-15T12:30:45.000Z",
"updatedAt": "2026-06-15T12:30:45.000Z"
}
],
"nextCursor": "MjAyNi0wNi0xNVQxMjozMDo0NS4wMDBafGFiYzEyMw"
} nextCursor is null on the last page. To fetch everything, follow the cursor
until it is null; to keep a local copy up to date afterwards, use Changes below instead of listing
again.
Search Bookmarks
GET /bookmarks/search?q=your+query
GET /bookmarks/search?q=github.com/sveltejs&type=keyword
GET /bookmarks/search?q=sourdough&tag=recipes Semantic search by default: finds bookmarks by meaning and returns the closest matches first.
It matches the bookmark itself (title, note, tags, URL) and, on plans with content indexing,
the text of the saved page. Bookmarks containing the query words are found too and rank
higher. Bookmarks below a relevance threshold that do not contain the words are left out, so
an empty list means nothing relevant was found. With type=keyword only the words are
matched against title, note, URL and tags: every word as a prefix, in any order, tolerating typos.
Query Parameters
q*required - The search query (max 500 characters)typeoptional -semantic(default) orkeywordlimitoptional - Maximum number of results, 1-100 (default 20)tag,untaggedoptional - Restrict the search to a tag selection, as in List Bookmarks
Response
Status: 200 OK, 400 Bad Request, or 429 Too Many Requests
data holds the matching bookmarks, best first. Semantic results carry a score (cosine similarity of the best match, higher means more similar), matchedBy (which signals matched: bookmark, summary, content, best first, then keyword when the bookmark contains the
query words) and snippet (the page summary or the best-matching passage when the
page content matched, otherwise null). Bookmarks imported moments ago are found
by their words at once and by meaning within a few minutes; until then they have no score.
Page State
Every bookmark carries a page object with what Pinport knows about the saved URL,
or null right after creation:
status-ok,thin(title and description only),pending,gone,unreachable,auth(login required),blocked,skipped,unsupportedhttpStatus,checkedAt- Last fetch attemptmovedTo- Redirect target when the page moved elsewhereattention-gone,unreachable,movedornull: whether the link needs attentiondismissed- The user chose to keep the link as is
List, tags and search accept attention=true to restrict to bookmarks that need
attention. PATCH /bookmarks/:id with { "attentionDismissed": true } hides the current attention state until the page fails again; false shows it
again. GET /bookmarks/:id/archive returns the Wayback Machine's closest snapshot
as { "url": "..." }, or null when nothing is archived. Links are checked
every 7 to 30 days depending on the plan, and a page counts as gone or unreachable only after two
failed checks on separate days.
Changes (Sync)
GET /bookmarks/changes?since=0
GET /bookmarks/changes?since=425790 Everything that changed after a sync version, oldest change first, for keeping a local copy in
sync without downloading the whole collection. Start with since=0, store the
returned version, and pass it as since next time. Deleted bookmarks are
reported as ids for 30 days; a client that has not synced for longer should start from 0 again.
Query Parameters
sinceoptional - Version from a previous response (default 0)limitoptional - Page size, 1-5000 (default 1000)
Response
Status: 200 OK or 400 Bad Request
{
"changed": [
{
"id": "abc123",
"url": "https://example.com",
"title": "Example Website",
"note": "",
"tags": ["reference"],
"createdAt": "2026-06-15T12:30:45.000Z",
"updatedAt": "2026-06-16T09:12:00.000Z"
}
],
"deleted": ["def456"],
"version": 425790,
"hasMore": false
} While hasMore is true, request again with since set to the returned version.
Tags
GET /tags
GET /tags?tag=recipes Your tags with the number of bookmarks carrying each. With a tag filter (same parameters as List Bookmarks), the counts cover only the matching bookmarks, which is what a tag sidebar needs.
Response
Status: 200 OK
{
"data": [ { "name": "recipes", "count": 42 }, { "name": "vegan", "count": 7 } ],
"untagged": 3,
"total": 120
} Topics
GET /bookmarks/topics
GET /bookmarks/topics?steps=[["topic","0c6e2b9d4f18"]]
GET /bookmarks/topics?steps=[["q","video"],["topic","3f9a1c0b7e42"],["tag","work"]] The topics Pinport finds in a bookmark list, and the list itself once narrowed. The list is
described by steps, a JSON array of [kind, value] pairs applied in order,
so a topic picked after a search splits only the search results, and a search after a topic looks
only within it. URL-encode the parameter.
Steps
["q", query]- Keep the bookmarks matching a search, best first (at most 5)["tag", name]- Keep the bookmarks carrying the tag["untagged", ""],["attention", ""]- Keep bookmarks without tags, or that need attention["topic", id]- Keep the bookmarks of a topic returned for the steps before it (at most 5)
Query Parameters
stepsoptional - The steps (default[], the whole library)limitoptional - Bookmarks returned indata, 1-100 (default 50)
Response
Status: 200 OK, 400 Bad Request, or 429 Too Many Requests
{
"topics": [
{ "id": "3f9a1c0b7e42", "label": "Color and contrast", "count": 14 },
{ "id": "a81d55e0c913", "label": "Icon sets", "count": 9 }
],
"applied": [ { "id": "0c6e2b9d4f18", "label": "Design tools" } ],
"ids": ["abc123", "def456"],
"data": [ { "id": "abc123", "url": "https://example.com", "title": "Example Website" } ]
} topics splits the narrowed list, largest first, and is empty when the list has
fewer than 12 bookmarks. applied lists the requested topics the list still forms,
with their names; a topic id is a fingerprint of its members, so it lapses when an earlier
step changes or the library regroups, and is then left out. ids (every remaining
bookmark, best first) and data (the first limit of them, as in Search)
are present only when a search or topic narrows the list; otherwise use List Bookmarks. Requests
are limited to 30 per minute per account, and topic names count against the plan's monthly naming
allowance.
Get a Bookmark
GET /bookmarks/:id Returns a single bookmark by ID.
Response
Status: 200 OK or 404 Not Found
Returns a bookmark object or an error if the bookmark doesn't exist.
Create a Bookmark
POST /api/v2/bookmarks
Content-Type: application/json
X-Api-Key: your-api-key
{
"url": "https://example.com",
"title": "Example Website",
"note": "Why I saved this",
"tags": ["example", "demo"]
} Creates a new bookmark.
Request Body Fields
url*required - The URL to bookmarktitleoptional - Title of the bookmark (taken from the page if not provided)noteoptional - Your note, max 250 characterstagsoptional - Array of tag strings
Response
Status: 201 Created or 400 Bad Request
Returns the created bookmark object or an error.
Update a Bookmark
PUT /api/v2/bookmarks/:id
Content-Type: application/json
X-Api-Key: your-api-key
{
"url": "https://example.com",
"title": "Updated Title",
"note": "Updated note",
"tags": ["updated", "tags"]
} Replaces a bookmark. url and title are required; a missing note or tags clears them.
Response
Status: 200 OK, 400 Bad Request, or 404 Not Found
Returns the updated bookmark object or an error.
Partially Update a Bookmark
PATCH /api/v2/bookmarks/:id
Content-Type: application/json
X-Api-Key: your-api-key
{
"title": "Updated Title",
"tags": ["updated", "tags"]
} Updates only specific fields of a bookmark. Only include the fields you want to modify.
Response
Status: 200 OK, 400 Bad Request, or 404 Not Found
Returns the updated bookmark object or an error.
Delete a Bookmark
DELETE /bookmarks/:id
X-Api-Key: your-api-key Deletes a bookmark.
Response
Status: 200 OK or 404 Not Found
Returns the deleted bookmark or an error if the bookmark doesn't exist.
Update Several Bookmarks
PATCH /api/v2/bookmarks
Content-Type: application/json
X-Api-Key: your-api-key
[
{ "id": "abc123", "tags": ["archive"] },
{ "id": "def456", "note": "" }
] Applies a partial update to each listed bookmark. Every id must exist, otherwise nothing
is changed.
Response
Status: 200 OK, 400 Bad Request, or 404 Not Found
{
"count": 2,
"bookmarkIds": ["abc123", "def456"]
} Delete Several Bookmarks
POST /api/v2/bookmarks/delete
Content-Type: application/json
X-Api-Key: your-api-key
{ "ids": ["abc123", "def456"] } Deletes the listed bookmarks. Every id must exist, otherwise nothing is deleted.
Response
Status: 200 OK, 400 Bad Request, or 404 Not Found
Returns count and bookmarkIds as in Update Several Bookmarks.
Import Bookmarks
POST /api/v2/bookmarks/import
Content-Type: application/json
X-Api-Key: your-api-key
{
"bookmarks": [
{ "url": "https://example.com", "title": "Example", "tags": ["imported"] },
{ "url": "https://example.org", "title": "Another", "note": "From my old manager" }
]
} Creates up to 2,000 bookmarks in one request. url and title are required
for each. Titles are not fetched from the pages, and semantic search over the imported bookmarks
becomes available within a few minutes.
Response
Status: 201 Created or 400 Bad Request
Returns data with the created bookmark objects, in the order they were sent.
Rename a Tag
PUT /api/v2/tags/:tag
Content-Type: application/json
X-Api-Key: your-api-key
{ "newName": "cooking" } Renames the tag on every bookmark carrying it. If the new name already exists on a bookmark, the two are merged.
Response
Status: 200 OK or 400 Bad Request
Returns count, bookmarkIds, oldTag and newTag.
Delete a Tag
DELETE /tags/:tag
X-Api-Key: your-api-key Removes the tag from every bookmark carrying it. The bookmarks themselves are kept.
Response
Status: 200 OK
Returns count, bookmarkIds and tag.
API v1 (deprecated)
Integrations built on https://pinport.app/api/v1 keep working unchanged until September 30, 2026: the bookmark field is called description there, GET /bookmarks returns the whole collection, and search is semantic only. v1 will not
receive new features and is switched off after that date. Please move integrations to v2 before then.