Skip to content

The Omamo API

The Omamo API lets your own scripts and tools read and save your links and notes. A key acts as you: it sees what you can see in Omamo, and it can change only what you can change. The API is free on every plan, inside the limits below.

For AI apps such as Claude or ChatGPT, use the MCP server instead. You sign in there, and no key is needed.

  1. Open Settings and find API keys.
  2. Press New key, and name it after the script or tool that will use it.
  3. Pick the access. Read only can list and read. Read and write can also save, change, move, and delete items, and make and rename boards and collections.
  4. Pick an expiry: 30 days, 90 days, a year, or never.
  5. Press Make key, and copy the key. Omamo shows it only this once.

If you signed in more than 15 minutes ago, Omamo asks you to sign in again before it makes a key. A key can act as you for months, so it needs a recent sign-in.

A key is omamo_ and 43 random characters. Omamo keeps only a fingerprint of it, a SHA-256 hash. Nobody at Omamo can read your key, and Omamo cannot show a lost key again. Make a new one instead.

Every path starts with https://omamo.app/api/public/v1. Send the key in the Authorization header:

export OMAMO_KEY="omamo_…"

curl "https://omamo.app/api/public/v1/spaces" \
  -H "Authorization: Bearer $OMAMO_KEY"
{
  "data": [
    { "id": "cmw2enyni005mlzqn5sacm6z2", "name": "Desk", "description": null, "boards": 3, "members": 1 }
  ]
}

Save a link into a collection. Omamo then reads the page for its title, picture, and description:

curl -X POST "https://omamo.app/api/public/v1/collections/<collectionId>/links" \
  -H "Authorization: Bearer $OMAMO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/", "tags": ["read later"]}'

Write a note. Each line of the text becomes a paragraph:

curl -X POST "https://omamo.app/api/public/v1/collections/<collectionId>/notes" \
  -H "Authorization: Bearer $OMAMO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title": "Ideas", "text": "First idea\nSecond idea"}'

The reference lists every endpoint with its fields. Spaces hold boards, boards hold collections, and collections hold items, so start with the spaces and follow the ids down.

A key never deletes spaces, boards, or collections, never invites people, and never reaches the trash, billing, your account, or your API keys. Do those in the app. Deleted items go to the trash, where you can restore them for 30 days.

Answers are JSON. A success carries data:

{ "data": { "id": "…", "title": "…" } }

A failure carries error, with a code your code can check and a message a person can read. For invalid_request, details names each field that is wrong:

{
  "error": {
    "code": "invalid_request",
    "message": "The request is not valid.",
    "details": [{ "path": "url", "message": "Invalid URL" }]
  }
}
Status Code What it means
400 invalid_request The path, query, or body is not valid.
401 unauthorized No key, or a key that is not valid, revoked, or expired.
402 payment_required Over your plan’s limits, or a Pro feature.
403 forbidden You cannot do this in Omamo, or the request came from a web page.
403 insufficient_scope A read only key tried to change something.
404 not_found It does not exist, or you cannot open it.
429 rate_limited Too many requests. Wait for the seconds in Retry-After.
500 server_error Something went wrong at Omamo. Try again.

The item lists come a page at a time. limit sets the page size, up to 100, and the default is 50. For each page after the first, send the nextCursor of the page before as cursor. On the last page, nextCursor is null. Treat the cursor as opaque: pass it back as it came.

const base = 'https://omamo.app/api/public/v1';
const headers = { Authorization: `Bearer ${process.env.OMAMO_KEY}` };

async function allItems(collectionId) {
  const items = [];
  let cursor = null;
  do {
    const query = new URLSearchParams({ limit: '100', ...(cursor ? { cursor } : {}) });
    const response = await fetch(`${base}/collections/${collectionId}/items?${query}`, { headers });
    if (!response.ok) throw new Error((await response.json()).error.message);
    const page = await response.json();
    items.push(...page.data);
    cursor = page.nextCursor;
  } while (cursor);
  return items;
}
  • Each key can make 120 requests a minute. Every answer carries RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset (seconds until the minute starts over).
  • Saving is also limited to 60 saves a minute for you, across the app, the extension, MCP, and the API.
  • Your plan’s limits apply, as in the app. On Free, you hold 200 saves across the spaces you own, and a save past that answers 402.
  • A body can be up to 2 MB, and a note’s text up to 100,000 characters.
  • Saved articles (GET /items/{itemId}/article) are part of Omamo Pro.

When you get 429, wait for the seconds in Retry-After, then try again.

  • Use keys only from servers, scripts, and command lines. Omamo refuses any request that comes from a web page, because a key in a web page is a key anyone can copy.
  • Keep the key in an environment variable or a secret store, not in your code or in git.
  • Give each script its own key. If a script only reads, make its key read only.
  • Pick an expiry. When a key expires, make a new one.
  • If a key leaks, revoke it in Settings. It stops working on its next request.

Settings shows the day each key was last used. Revoke the keys that no script uses.

This is version 1. Omamo can add endpoints and add fields to answers without warning, so ignore fields you do not know. A change that breaks code will come as version 2, at a new address, and version 1 will keep working for a while.

The OpenAPI file describes the same endpoints for tools that read OpenAPI.