API overview

The Trovemarks API lets your own scripts and tools save links, read your library and organise collections. It’s the same API the web app, the extension and Raycast use.

https://trovemarks.com/api/v1

Everything here works on every plan. The reference lists every endpoint, and the OpenAPI document describes them for HTTP clients like Postman, Insomnia or Bruno: import it, and every request is ready to try.

Keys

  1. Go to Settings → Sources and choose + New API key.
  2. Name it after what will use it, like Newsletter script.
  3. Copy the key. It starts with tm_, and it’s shown once.
The new API key dialog
Name a key after what will use it. Copy it now: it won’t be shown again.

Send it in the Authorization header of every request:

curl https://trovemarks.com/api/v1/me \
  -H "Authorization: Bearer tm_…"

Each key is a source: links it saves remember it, and you can give it defaults (a collection, tags, public or private) or pause it, without touching your code. Disconnect it in Settings → Sources to revoke it at once.

Keys can’t change your account, passkeys, email, sources or billing; those need you signed in on the web.

Your first request

Save a link:

curl https://trovemarks.com/api/v1/bookmarks \
  -H "Authorization: Bearer tm_…" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/article", "tags": ["reading"], "collection_names": ["To read"]}'

It returns 201 with the new link (bookmark) straight away. The page’s title, description and icon are fetched in the background, so metadata is "pending" at first; fetch the link again a few seconds later to see them. Saving a link you already have returns 200 with the existing one, and adds any new tags and collections.

collection_names finds collections by name (ignoring case) and creates missing ones, so you don’t need to look up ids first.

Conventions

  • JSON in, JSON out. Send Content-Type: application/json with a body.
  • snake_case everywhere: collection_ids, next_cursor, created_at.
  • Ids are UUIDs. Times are ISO 8601 in UTC, like 2026-01-31T09:00:00.000Z.
  • Unknown query parameters are rejected with a 400, so typos don’t silently return everything.
  • New fields may be added to responses at any time. Ignore ones you don’t know.
  • Private links are included in everything a key reads, because they’re yours.

Paging

Lists return a page at a time:

{
  "data": [ … ],
  "next_cursor": "eyJ0IjoiMjAy…"
}

Pass next_cursor back as cursor to get the next page. It’s null on the last page. Set limit to choose the page size; each list says its default and maximum.

curl "https://trovemarks.com/api/v1/bookmarks?limit=100&cursor=eyJ0IjoiMjAy…" \
  -H "Authorization: Bearer tm_…"

Filtering lists

GET /bookmarks takes the same filters as the library. List parameters take several values, comma-separated:

GET /bookmarks?collection=<id>,<id>&tag=<id>&visibility=public&saved_after=2026-01-01&q=pricing

See List your links for every filter.

Errors

Errors have a status code and a body like this:

{
  "error": {
    "code": "invalid_request",
    "message": "Some fields are invalid.",
    "details": {
      "form_errors": [],
      "field_errors": { "private": ["Invalid input: expected boolean, received string"] }
    }
  }
}

code is stable; check it in your code. message is written for people and may change. Invalid requests list what was wrong with each field in details.field_errors.

Status Codes you might see Means
400 invalid_request, bad_request Something in the request isn’t right.
401 unauthorized, invalid_token No key, or the key is wrong or revoked.
402 pro_required That needs Pro, like more than 10 collections.
403 forbidden, session_required A key can’t do that; it needs you signed in on the web.
404 not_found It doesn’t exist, or isn’t yours.
409 title_taken, slug_taken, source_paused It clashes with something, or the key’s source is paused.
429 rate_limited Too many requests. Wait for Retry-After seconds.
5xx Our fault. Try again in a moment.

Rate limits

Each account can make 300 requests a minute, across all its keys and the web app. Every response has a RateLimit-Remaining header. Past the limit you get a 429 with a Retry-After header saying how many seconds to wait.

Need to change many links? One bulk request can change up to 10,000 at once, and counts as one request.

Beyond keys

  • AI assistants connect through the MCP server, which signs people in with OAuth. You don’t need a key for that.
  • RSS: public collections have feeds anyone can read without a key.
  • Need something the API doesn’t do yet? Tell us at [email protected].